【Programming Pearls】查找一段文本或單詞中的最長重複子串

           給定一個文本文件,查找最長的重複子串。

 如文本“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;

}

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章