题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4763
题目描述:
现有一字符串S,要求在S中找到最长的子串E,使得S满足格式“EAEBE”,其中A,B可以为任意的S子串。也就是说子串E既是S的前缀也是S的后缀,同时还在S中间出现,但不与前缀E与后缀E重叠。
解题思路:
学习KMP的第一道题。KMP的详解这篇文章写得很好:http://www.cnblogs.com/c-cloud/p/3224788.html,看完应该能理解前缀后缀概念和next数组的意义了(顺便给自己备忘下)。
好,那么有了上述储备知识后就可以来解题啦。已知next[i]表示的是字符串str中的一段,即“str[0]str[1] ... str[i-1]str[i]”这个字符串,的相同前后缀的最长长度,那么要在字符串S中找E,就直接从S的末位开始,假设k = next[S.length()-1],那么也就是说存在前缀“S[0]S[1] ... S[k-1]”与后缀“S[S.length()-k] ... S[S.length()-1]”相等,那么直接在S的子串“S[k]S[k+1] ... S[S.length()-k-1]”中寻找那段前缀就可以了。如果没有的话就减小前后缀长度重新在新中间段中寻找即可。
嘛,说的不是很清楚,仅供参考用...
代码:
1 //#include <iostream> 2 #include <cmath> 3 #include <cstdio> 4 #include <cstring> 5 //#include <vector> 6 //#include <algorithm>//C++中限定字符有next,用C++的库的话oj会对next报错 7 using namespace std; 8 9 #define ll long long 10 #define INF 0x3f3f3f3f3f 11 #define MAX 1001000 12 13 char T[MAX], S[MAX]; 14 int next[MAX]; 15 16 /*void display(int n) 17 { 18 for(int i = 0; i < n; ++i) { 19 printf("%d ", next[i]); 20 } 21 printf("\n"); 22 }*/ 23 24 void getNext() 25 { 26 next[0] = 0; 27 int len = strlen(T); 28 int k = 0; 29 for(int i = 1; i < len; ++i) { 30 while(k > 0 && T[i] != T[k]) 31 k = next[k-1]; 32 if(T[i] == T[k]) 33 k++; 34 next[i] = k; 35 } 36 } 37 38 int main() 39 { 40 //freopen("debug\\in.txt", "r", stdin); 41 //freopen("CON", "w", stdout); 42 int i, j, k; 43 int test; 44 scanf("%d", &test); 45 while(test--) { 46 scanf("%s", T); 47 memset(next, 0, sizeof(next)); 48 getNext(); 49 int len = strlen(T); 50 //display(len); 51 52 int ans = 0; 53 j = next[len - 1]; //获取整个字符串的最长相同前后缀长度 54 bool flag = 0; //flag=1时表明找到题目要求的字符串了,跳出循环 55 for(i = j; i >= 0; --i) { //从最长相同前后缀长度开始递减 56 k = len - i - 1; //中间段的下界,同时也是搜寻下标 57 while(next[k] == 0 && k >= 2 * i - 1) //寻找中间段最右边next值不为0的字符 58 k--; 59 while(k >= i * 2 - 1) { //明显中间的子串不能跟前缀重叠 60 if(next[k] >= i) { //找到子串了 61 ans = i; 62 flag = 1; 63 break; 64 } 65 else 66 k = next[k] - 1; //否则向前继续寻找 67 } 68 if(flag) break; 69 } 70 printf("%d\n", ans); 71 } 72 }
时间: 2024-12-21 08:17:58