zoukankan      html  css  js  c++  java
  • 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
     */
  • 相关阅读:
    ioi1998 Polygon
    [Noip模拟题]Seq
    [noip模拟]分组行动
    入门OJ:photo
    Sgu167 I-country
    入门OJ:简单的网络游戏
    入门OJ:Coin
    ATT&CK实战系列
    Metasploit Framework(二)
    RoarCTF 2019
  • 原文地址:https://www.cnblogs.com/lc520love/p/5288659.html
Copyright © 2011-2022 走看看