【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;

}

 


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值