编程珠玑:用后缀数组寻找最长重复字符串
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; }