算法编程题:
1、给定一个字符串,求出其最长的重复子串。思路:使用后缀数组,对一个字符串生成相应的后缀数组后,然后再排序,排完序依次检测相邻的两个字符串的开头公共部分。这样的时间复杂度为:
生成后缀数组 O(N)排序 O(NlogN*N) 最后面的 N 是因为字符串比较也是 O(N)依次检测相邻的两个字符串 O(N * N)总的时间复杂度是 O(N^2*logN),
解答:
对于类似从给定的文本中,查找其中最长的重复子字符串的问题,可以采用“后缀数组”来高效地完成此任务。后缀数组使用文本本身和n个附加指针(与文本数组相应的指针数组)来表示输入文本中的n个字符的每个子字符串。 首先,如果输入字符串存储在c[0..n-1]中,那么就可以使用类似于下面的代码比较每对子字符串: maxlen = -1 for i = [0, n) for j = (i, n) if (thislen = comlen(&c[i], &c[j])) > maxlen maxlen = thislen maxi = i maxj = j 当作为comlen函数参数的两个字符串长度相等时,该函数便返回这个长度值,从第一个字符开始: int comlen(char *p, char* q) i = 0 while *p && (*p++ = *q++) i++ return i 由于该算法查看所有的字符串对,所以它的时间和n的平方成正比。下面便是使用“后缀数组”的解决办法。 如果程序至多可以处理MAXN个字符,这些字符被存储在数组c中: #define MAXN 5000000 char c[MAXN], *a[MAXN]; 在读取输入时,首先初始化a,这样,每个元素就都指向输入字符串中的相应字符: while (ch = getchar()) != EOF a[n] = &c[n]; c[n++] = ch; c[n] = 0 //将数组c中的最后一个元素设为空字符,以终止所有字符串。 这样,元素a[0]指向整个字符串,下一个元素指向以第二个字符开始的数组的后缀,等等。如若输入字符串为"banana",该数组将表示这些后缀: a[0]:banana a[1]:anana a[2]:nana a[3]:ana a[4]:na a[5]:a 由于数组a中的指针分别指向字符串中的每个后缀,所以将数组a命名为"后缀数组" 第二,对后缀数组进行快速排序,以将后缀相近的(变位词)子串集中在一起 qsort(a, n, sizeof(char*), pstrcmp)后 a[0]:a a[1]:ana a[2]:anana a[3]:banana a[4]:na a[5]:nana 第三,使用以下comlen函数对数组进行扫描比较邻接元素,以找出最长重复的字符串: for i = [0, n) if comlen(a[i], a[i+1]) > maxlen maxlen = comlen(a[i], a[i+1]) maxi = i printf("%.*s/n", maxlen, a[maxi]) 由于少了内层循环,只是多了一次排序,因此该算法的运行时间为O(n logn).
#include#include #include #define MAXCHAR 5000 //最长处理5000个字符 char c[MAXCHAR], *a[MAXCHAR]; int comlen( char *p, char *q ){ int i = 0; while( *p && (*p++ == *q++) ) ++i; return i; } int pstrcmp( const void *p1, const void *p2 ){ return strcmp( *(char* const *)p1, *(char* const*)p2 ); } int main( ){ char ch; int n=0; int i, temp; int maxlen=0, maxi=0; printf("Please input your string:\n"); while( (ch=getchar())!='\n' ){ a[n]=&c[n]; c[n++]=ch; } c[n]='\0'; qsort( a, n, sizeof(char*), pstrcmp ); for(i=0; i maxlen ){ maxlen=temp; maxi=i; } } printf("%.*s\n",maxlen, a[maxi]); system("PAUSE"); return 0; }