1.基本概念
子串:字符串 S 的子串 r[i..j] , i ≤ j ,表示 r 串中从 i 到 j 这一段,就是顺次排列 r[i],r[i+1],...,r[j] 形成的字符串。
后缀:后缀是指从某个位置 i 开始到整个串末尾结束的一个特殊子串。字符串 r 的从 第 i 个字 符 开 始 的 后 缀 表 示 为 Suffix(i) ,也 就 是Suffix(i)=r[i..len(r)] 。
后缀数组:后缀数组 SA 是一个一维数组,它保存 1..n 的某个排列 SA[1] ,SA[2] , …… , SA[n] ,并且保证 Suffix(SA[i]) < Suffix(SA[i+1]) , 1 ≤ i<n 。也就是将 S 的 n 个后缀从小到大进行排序之后把排好序的后缀的开头位置顺次放入 SA 中。
2.问题描述
给定一个文本文件作为输入,查找其中最长的重复子字符串。例如,"Ask not what your country can do for you, but what you can do for your
country"中最长的重复字符串是“can do for you”,第二长的是"your country"。
3.解决思路
利用后缀数组。首先输入一个字符串到c[]中,例如“banana”,读入时我们队指针的数组a进行初始化,使得每个元素指向输入字符串的相应字符,则元素a[0]指向整个字符串,下一个元素指向从第二个字符开始的数组后缀,等等。对于前面的输入字符串,该数组能够表示下面这些后缀:
a[0]:banana
a[1]:anana
a[2]:nana
a[3]:ana
a[4]:na
a[5]:a
如果某个长字符串在数组a中出现两次,那么她将出现在两个不同的后缀中,因此我们队数组排序以寻找相同的后缀,下面将上面的数组a进行数组排序,结果如下
a[0]:a
a[1]:ana
a[2]:anana
a[3]:banana
a[4]:na
a[5]:nana
然后我们就可以扫描数组,通过比较相邻元素来找出最长的重复字串,如上为"ana"
4.代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 5000000
char c[MAXN], *a[MAXN];
int comlen(char *p, char *q)
{
int i = 0;
while(*p && (*p++ == *q++))
{
i++;
}
return i;
}
int pstrcmp(const void *a, const void *b)
{
return strcmp(*(char* const*)a, *(char* const*)b); //这里的 *(char* const*)a中的a 为指向指针的指针,
} //首先(char* const*)a 将变量a强制转换成char类型的指向指针的const指针,
//然后用*进行 地址解引用
int main()
{
char ch;
int i,n=0,maxlen=-1,maxi;
while((ch = getchar()) != EOF && ch != '\n')
{
a[n] = &c[n];
c[n++] = ch;
}
c[n]='\0';
qsort(a, n , sizeof(char *), pstrcmp);
for(i = 0;i < n-1; i++)
{
if(comlen(a[i], a[i+1]) > maxlen)
{
maxlen = comlen(a[i], a[i+1]);
maxi = i;
}
}
printf("%.*s\n", maxlen, a[maxi]);
system("pause");
return 0;
}