給定一個文本文件,查找最長的重複子串。
如文本“Ask not what your country can do for you, but what you can do for your country”,最長的重複子串爲“can do for you”和“your country”。單詞banana的最長重複子串爲“ana“。該問題可以看成是一個由顛倒字母組成的單詞組合問題。假設該輸入字符串保存在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
由於上面的算法要比較所有可能的子串對,所以時間複雜度比較高,爲o(n^2)。我們可以採用“後綴數組“來降低它的時間複雜度。
假設處理的文本的字符總數最多爲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
其中a[0]指向整個字符串,a[1]指向從第二個字符開始往後的所有字符,依次類推。如下:
a[0]: banana
a[1]: anana
a[2]: nana
a[3]: ana
a[4]: na
a[5]: a
如果一個長字符串在C中出現兩次,它們的後綴不相同。然後按照字母順序對數組a的字符串進行排序,然後比較相鄰的字符串,找出最長的重複子串爲ana。
a[0]: a
a[1]: ana
a[2]: anana
a[3]: banana
a[4]: na
a[5]: nana
這裏採用qsort對“後綴數組“進行排序。
qsort(a, n, sizeof(char *), pstrcmp);
用下面的代碼比較相鄰字符串的最大長度:
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])
完整的代碼如下:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int pstrcmp(char **p, char **q)
{ return strcmp(*p, *q); }
int comlen(char *p, char *q)
{ int i = 0;
while (*p && (*p++ == *q++))
i++;
return i;
}
#define M 1
#define MAXN 5000000
char c[MAXN], *a[MAXN];
int main()
{ int i, ch, n = 0, maxi, maxlen = -1;
while ((ch = getchar()) != EOF) {
a[n] = &c[n];
c[n++] = ch;
}
c[n] = 0;
qsort(a, n, sizeof(char *), pstrcmp);
for (i = 0; i < n-M; i++)
if (comlen(a[i], a[i+M]) > maxlen) {
maxlen = comlen(a[i], a[i+M]);
maxi = i;
}
printf("%.*s\n", maxlen, a[maxi]);
return 0;
}