<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0074)http://www.cs.concordia.ca/ccss/events/coding_compet_2002/codingprob1.html --><HTML><HEAD><script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- MyFirstUnitAd -->
<ins class="adsbygoogle"
     style="display:inline-block;width:970px;height:250px"
     data-ad-client="ca-pub-5778386704669218"
     data-ad-slot="1503492166"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<TITLE>CSSS Coding Competition</TITLE>
<META http-equiv=Content-Type content="text/html; charset=gb2312"><LINK rev=made 
href="mailto:grogono@cs.concordia.ca">
<META content="MSHTML 6.00.2800.1141" name=GENERATOR>
<META content="Peter Grogono" name=author>
<META content="Course Description" name=description>
<META content=Keywords name=keywords>
<STYLE type=text/css>P {
	FONT-FAMILY: Arial, Helvetica, Geneva, sans-serif
}
UL {
	FONT-FAMILY: Arial, Helvetica, Geneva, sans-serif
}
LI {
	FONT-FAMILY: Arial, Helvetica, Geneva, sans-serif
}
TABLE {
	FONT-FAMILY: Arial, Helvetica, Geneva, sans-serif
}
TR {
	FONT-FAMILY: Arial, Helvetica, Geneva, sans-serif
}
TD {
	FONT-FAMILY: Arial, Helvetica, Geneva, sans-serif
}
</STYLE>
</HEAD>
<BODY text=#800000 vLink=#800080 aLink=#ff0000 link=#0000ff bgColor=#f5deb3>
<H1 align=center>CSSS Coding Competition -- October 2002</H1>
<HR>

<H2>Problem 1</H2>
<P>A <B>rule</B> <I>(x,y)</I> consists of two strings of letters <I>x</I> and 
<I>y</I>. A rule <I>(x,y)</I> can be <B>applied</B> to a string <I>s</I> if 
<I>x</I> is a substring of <I>s</I>, in which case <I>x</I> is replaced by 
<I>y</I> in <I>s</I>. For example, the rule <I>(ab,ba)</I> can be applied to the 
string "abcab" in two ways: replacing the first occurrence of "ab" gives "bacab" 
and replacing the second occurrences of "ab" gives "abcba". 
<P>Given a set of rules, a source string, and a target string, the problem is to 
find the shortest sequence of rules that transform the source string into the 
target string. 
<P>The input file consists of: the number of rules, (four in this example); the 
rules, one on each line; the source string; and the target string. 
<P>The output file consists of: the source string; the intermediate strings 
obtained by applying one rule at a time; and the target string, with each string 
on a separate line. 
<P>Here is a typical input file: <PRE>        4
        ab ba
        ab bcd
        aa d
        db bb
        abab
        bbb
</PRE>
<P>And this is the corresponding output: <PRE>        abab
        baab
        bdb
        bbb
</PRE>
<HR>

<H3>Test Data</H3>
<P>First: <PRE>4
ab bcd
ab ba
cdc a
d bb
abab
bbbba
</PRE>
<P>Expected output: <PRE>abab
abbcd
babcd
bbcdcd
bbad
bbabb
bbbab
bbbba
</PRE>
<P>Second: <PRE>5
a ab
ab ba
baa bab
ba ab
bab bcb
abaaaa
bcbcbcb
</PRE>
<P>Expected output: <PRE>abaaaa
abbaaaa
babaaaa
bababaa
bababab
bcbabab
bcbcbab
bcbcbcb
</PRE>
<HR>

<H3>Discussion</H3>
<P>Things to watch out for in this problem are: exponential explosion (several 
new strings can be derived from each string generated) and looping. There may be 
"smart" solutions to this problem but there isn't enough time to try them out in 
a competition. The best strategy is to start with a "brute force" technique and 
hope that the test data sets are not too large. 
<P>Imagine a tree with the starting string at the root; the subtrees of a node 
are the result of applying each applicable rule to that node. The target string 
must be somewhere in the tree. Depth first search will almost certainly fail, 
since there will be many infinite paths in the tree. But breadth first search 
will find the target string and also provide the shortest path to it. 
<P>One way to implement breadth first search is to set up a queue. The first 
entry of the queue is the start string. We retrieve a queue entry, apply the 
rules to it, and insert the new strings into the queue. To avoid looping, we 
never put the same string into the queue twice. When the target string appears 
in the queue, we are finished. 
<P>Fortunately, I coded the solution and tried it before the competition. In its 
original form, my second test took several minutes of CPU time! I simplified the 
problem, and the second data set above requires about 20 seconds of CPU time, 
during which it puts 4,942 strings in the queue. 
<P>The remaining problem is to print out the intermediate strings. To do this, 
we add a pointer to each queue entry, pointing back to the string from which 
this string was derived. We can then use a recursive function 
(<CODE>report()</CODE> below) to print out the intermediate strings in the 
correct sequence. 
<P>Although this problem required the most code, I do not consider it the most 
difficult problem of the four (although that may be because I wrote it myself!). 
The solution uses standard algorithms and data structures, and can be written 
quite quickly. However, this is the only problem for which I actually introduced 
classes, because I felt that I needed an abstraction to avoid getting bogged 
down in too many levels of detail. 
<HR>

<H3>Code</H3><PRE>#include &lt;iostream.h&gt;
#include &lt;fstream.h&gt;
#include &lt;string.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

ifstream in;
ofstream out;

class Node
{
public:
   Node(char *str, Node *from = NULL, Node *next = NULL)
      : str(str), from(from), next(next) {}
   char *str;
   Node *from;
   Node *next;
};

class Queue
{
public:
   Queue(char *str)
   {
      first = new Node(str);
      last = first;
      curr = NULL;
   }
   Node *has(char *patt)
   {
      Node *p = first;
      while (p)
      {
         if (strcmp(p-&gt;str, patt) == 0)
            return p;
         p = p-&gt;next;
      }
      return NULL;
   }
   Node *next() 
   {
      if (curr == NULL)
         curr = first;
      else
         curr = curr-&gt;next;
      return curr;
   }
   void add(char *str, Node *from)
   {
      if (has(str))
         return;
      Node *n = new Node(str, from, NULL);
      last-&gt;next = n;
      last = n;
   }
   void show()
   {
      cout &lt;&lt; "Queue: ";
      Node *p = first;
      while (p)
      {
         cout &lt;&lt; p-&gt;str &lt;&lt; " ";
         p = p-&gt;next;
      }
      cout &lt;&lt; endl;
   }
private:
   Node *first;
   Node *last;
   Node *curr;
};

char *readstr()
{
   const int LEN = 200;
   char buffer[LEN];
   in &gt;&gt; buffer;
   int len = strlen(buffer);
   char *p = new char[len + 1];
   strcpy(p, buffer);
   return p;
}

const int MAX = 100;
char *src[MAX];
char *dst[MAX];
char *start;
char *target;

bool match(char *p, char *q)
{
   while (true)
   {
      if (*p == '\0')
      {
         return true;
      }
      if (*q == '\0')
      {
         return false;
      }
      if (*p != *q)
      {
         return false;
      }
      p++;
      q++;
   }
}

char *replace(char *patt, char *p, char *src, char *dst)
{
   char buff[100];
   char *b = buff;
   while (patt &lt; p)
      *b++ = *patt++;
   for (char *q = src; *q; q++)
      patt++;
   char *d = dst; 
   for (q = dst; *q; q++)
      *b++ = *d++;
   while (*patt)
      *b++ = *patt++;
   *b++ = '\0';
   char *r = new char(strlen(buff) + 1);
   strcpy(r, buff);
   return r;
}

void report(Node *p)
{
   if (p)
   {
      report(p-&gt;from);
      out &lt;&lt; p-&gt;str &lt;&lt; endl;
   }
}

void main(int argc, char *argv[])
{
   in.open(argv[1]);
   out.open(argv[2]);
   int numrules;
   in &gt;&gt; numrules;
   for (int n = 0; n &lt; numrules; n++)
   {
      src[n] = readstr();
      dst[n] = readstr();
   }
   start = readstr();
   target = readstr();

   Queue q(start);
   while (true)
   {
      if (q.has(target))
      {
         report(q.has(target));
         break;
      }
      Node *s = q.next();
      if (s == NULL)
         break;
      char *patt = s-&gt;str;
      for (n = 0; n &lt; numrules; n++)
      {
         for (char *p = patt; *p; p++)
         {
            if (match(src[n], p))
            {
               q.add(replace(patt, p, src[n], dst[n]), s);
            }
         }
      }
   }
}
</PRE></BODY></HTML>
