转自:http://dsqiu.iteye.com/blog/1701324
参考:
勇幸|Thinking: http://www.ahathinking.com/archives/121.html
huang12315: http://blog.csdn.net/huang12315/article/details/6455090
unixfy: http://www.cppblog.com/unixfy/archive/2011/05/23/146986.html
勇幸|Thinking: http://www.ahathinking.com/archives/123.html
§1最长重复子串
1.1问题描述
首先这是一个单字符串问题。子字符串R 在字符串L 中至少出现两次,则称R 是L 的重复子串。重复子串又分为可重叠重复子串和不可重叠重复子串。
1.2基本方法
枚举子串,让子串和子串进行比较。直接看代码:
/* 最长重复子串 Longest Repeat Substring */ int maxlen; /* 记录最长重复子串长度 */ int maxindex; /* 记录最长重复子串的起始位置 */ void outputLRS(char * arr); /* 输出LRS */ /* 最长重复子串 基本算法 */ int comlen(char * p, char * q) { int len = 0; while(*p && *q && *p++ == *q++) { ++len; } return len; } void LRS_base(char * arr, int size) { for(int i = 0; i < size; ++i) { for(int j = i+1; j < size; ++j) { int len = comlen(&arr[i],&arr[j]); if(len > maxlen) { maxlen = len; maxindex = i; } } } outputLRS(arr); }
优化思路
一般的优化方法就是在充分利用已有的结果,最长重复子串的长度增加一定是建立在之前已经找到的重复子串之上的,充分利用已找到的重复子串的位置和长度是优化的一个重点,此外还有就是未不是重复子串的,以后就不会被包含在重复子串内,如"ab"只有一个,则重复子串就不能包含"ab"(允许重叠的重复子串例外)。
1.2KMP算法求解
对KMP算法还不是很了解的,可以查看我的另一篇博文(不懂猛点),在KMP算法的关键就是求解next数组,针对next[j]=k,可以得到P[0,1,...,k-1]=P[j-k,j-k+1,...,j-1]。看到P[0,1,...,k-1]=P[j-k,j-k+1,...,j-1]应该会眼前一亮,大脑顿时清醒些,这不就是重复子串吗!由此求解最长重复子串就转化为求解KMP算法next数组中的最大值(即max{next[j]=k}。
现在就是求解next数组的问题了,还是直接查看代码
int getNext(char *str,int *next) { int len=strlen(str); int index=0; int k=-1; next[0]=k; int max=0; //kmp算法求next值,取得最大的字串 while (index<len) { if (k==-1 || str[index]==str[k]) { k++; index++; next[index]=k; if (k>max)//求得其中重复最大的字串的个数,也就是与最前面串的重复数 { max=k; } } else k=next[k]; } return max; } int main() { char str[50];//输入字符串 cin>>str; int max=0;//最大的字串 int nextMax;//接受getNext函数中返回的最大值 int index; int maxIndex;//保存最大的字串起始位置 int len=strlen(str); //将一个字符串从开始一直减少到只剩一个字符,通过这个取得最小字串 for (index=0;index<len-1;index++) { int *next=new int[len-index];//取得next在这没用 nextMax=getNext(str+index,next);//每次从str+index开始 if (nextMax>max) { max=nextMax; maxIndex=index; } } //输出最长字串 cout<<"最长字串: "; for (index=0;index<max;index++) { cout<<str[index+maxIndex]; } cout<<endl; return 0; }
1.3后缀数组求解
后缀数组在我的另外一篇博文有介绍,还没有概念的可以移步查看点击。后缀数组就是保留字符串所有位置到字符串末尾的子字符串,a[i]就是第i位置到末尾的子串。有了后缀数组,对后缀数组进行排序,然后进行求后缀数组相邻元素的最大前缀就是最大重复子串。
#include <iostream> using namespace std; const int MAXN = 1000; int mycmp(const void* p1, const void* p2) { return strcmp(*(char* const*)p1, *(char* const*)p2); } int getLen(char* p, char* q) { int ret = 0; while (*p && *p++ == *q++) { ++ret; } return ret; } char* foo(char result[], char s[]) { int len = strlen(s); char** suffix = new char*[len]; for (int i = 0; i < len; ++i) { suffix[i] = s + i; } qsort(suffix, len, sizeof (char*), mycmp); //for (int i = 0; i < len; ++i) //{ // cout << suffix[i] << endl; //} int maxlen = 0, maxi = 0, maxj = 0, temp = 0; for (int i = 0; i < len - 1; ++i) { temp = getLen(suffix[i], suffix[i + 1]); if (temp > maxlen) { maxlen = temp; maxi = i; maxj = i + 1; } } //cout << maxlen << endl; //cout << suffix[maxi] << endl; //cout << suffix[maxj] << endl; //printf("%.*s ", maxlen, suffix[maxi]); for (int i = 0; i < maxlen; ++i) { result[i] = suffix[maxi][i]; } result[maxlen] = '