HDU 4763

题目链接:

      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]”中寻找那段前缀就可以了。如果没有的话就减小前后缀长度-->原来程序减小的方法错误,直接递减是错误的,想差了,要i = next[i-1]才行,i即是前后缀相同的长度,这样的减小方式才能保证前后缀始终是一样,然后重新在新中间段中寻找即可。

  嘛,说的不是很清楚,仅供参考用...

代码:

//#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
//#include <vector>
//#include <algorithm>//C++中限定字符有next,用C++的库的话oj会对next报错
using namespace std;

#define ll long long
#define INF 0x3f3f3f3f3f
#define MAX 1001000

char T[MAX], S[MAX];
int next[MAX];

/*void display(int n)
{
    for(int i = 0; i < n; ++i) {
        printf("%d ", next[i]);
    }
    printf("
");
}*/

void getNext()
{
    next[0] = 0;
    int len = strlen(T);
    int k = 0;
    for(int i = 1; i < len; ++i) {
        while(k > 0 && T[i] != T[k])
            k = next[k-1];
        if(T[i] == T[k])
            k++;
        next[i] = k;
    }
}

int main()
{
    //freopen("debug\in.txt", "r", stdin);
    //freopen("CON", "w", stdout);
    int i, j, k;
    int test;
    scanf("%d", &test);
    while(test--) {
        scanf("%s", T);
        memset(next, 0, sizeof(next));
        getNext();
        int len = strlen(T);
        //display(len);

        int ans = 0;
        i = next[len - 1]; //获取整个字符串的最长相同前后缀长度i
        bool flag = 0; //flag=1时表明找到题目要求的字符串了,跳出循环
        while (!flag && i > 0) { //从这里开始,跳出循环条件为找到了字符串或者选取的长度还大于0
            k = len - i - 1; //中间段的下界,同时也是搜寻下标
            while(next[k] == 0 && k >= 2 * i - 1) //寻找中间段最右边next值不为0的字符
                k--;
            while(k >= i * 2 - 1) { //明显中间的子串不能跟前缀重叠
                if(next[k] >= i) { //找到子串了
                    ans = i;
                    flag = 1;
                    break;
                }
                else if (next[k] - 1 > 0)
                    k = next[k] - 1; //否则向前继续寻找
                else
                    k--; //特判一下防止错过
            }
            i = next[i-1]; //因为我的字符串下标是0 - n-1,所以是next[i-1]
        }
        printf("%d
", ans);
    }
}
/*
这里给一组杭电题目讨论区 @我嘞个娘哦 提供的数据做为参考

10
aaaaaaaaa
aaaaba
ababab
ababdabab
abdabdabdab
aaabaabaaa
abababab
abdaab
abdabcab
abcabcabc

output:
3
1
2
2
2
1
2
0
2
3
 */
原文地址:https://www.cnblogs.com/lc520love/p/5288659.html