HDU 4763 Theme Section extkmp

来源:互联网 发布:我的淘宝等级是什么 编辑:程序博客网 时间:2024/06/05 10:34

题目:

http://acm.hdu.edu.cn/showproblem.php?pid=4763

题意:

给定一个字符串,找到一个子串,使得字符串呈现出"EAEBE"这样的形式,其中"E"是要找的子串,"A""B"可以不存在,求这个子串的最大长度

思路:

首先求出exkmpNext数组,接下来就可以用Next数组来求解。可以发现首部和尾部的子串是固定的,我们可以枚举中间子串的位置,然后枚举在这个位置上子串的长度1Next[i],首部子串肯定等于枚举子串,判断尾部字串是否等于枚举的子串和三个子串是否满足不重叠。以上就能过了,但是可以进一步优化,在枚举当前位置上子串的长度时,一定是在1Next[i]之间,但是不能直接二分,因为尾部子串不满足二分性质,可以发现尾部子串的长度一定不超过字符串长度的13,故把最后13字符串中的i+Next[i]=lenNext[i]存起来,其余的不能作为尾部子串,然后中间子串的长度一定在这些存起来的Next[i]中,而且此时满足二分性质,就可以二分求解了

#include <bits/stdc++.h>using namespace std;const int N = 1000000 + 10;char ori[N];int Next[N];int a[N];void get_next(char *pat){    int len = strlen(pat);    Next[0] = len;    int k = 0;    while(k + 1 < len && pat[k] == pat[k+1]) ++k;    Next[1] = k;    k = 1;    for(int i = 2; pat[i]; i++)    {        if(i + Next[i-k] < k + Next[k]) Next[i] = Next[i-k];        else        {            int j = max(k + Next[k] - i, 0);            while(i + j < len && pat[i+j] == pat[j]) ++j;            Next[i] = j;            k = i;        }    }}int main(){    int t;    scanf("%d", &t);    while(t--)    {        scanf("%s", ori);        get_next(ori);        int len = strlen(ori);        int k = 0;        for(int i = len - len/3; i < len; i++)            if(i + Next[i] == len) a[++k] = Next[i];        int ans = 0;        for(int i = 0; ori[i]; i++)        {            int l = 1, r = k, tmp = 0;            while(l <= r)            {                int mid = (l + r) >> 1;                if(a[mid] > Next[i]) l = mid + 1;                else if(0+a[mid]-1 < i && i+a[mid]-1 < len-a[mid] && len-a[mid] + Next[len-a[mid]] == len)                    tmp = a[mid], r = mid - 1;                else l = mid + 1;            }            ans = max(ans, tmp);        }        printf("%d\n", ans);    }    return 0;}

kmp算法也可以写。首先打表Next数组,Next[i]意义是str[0,Next[i1]]=str[iNext[i]+1,i],那么从Next[len]开始,去检查是否有满足条件的中间子串,然后len=Next[len]这样一直迭代下去
,这种写法很快,在这题没什么体现,大概是数据太水,在51nod1286体现明显

#include <bits/stdc++.h>using namespace std;const int N = 1000000 + 10;char ori[N], pat[N];int Next[N];void get_next(char *pat){    int i = 0, j = -1;    Next[0] = -1;    while(pat[i])    {        if(j == -1 || pat[i] == pat[j]) Next[++i] = ++j;        else j = Next[j];    }}int kmp(char *ori, char *pat){    int i = 0, j = 0;    while(ori[i])    {        if(j == -1 || ori[i] == pat[j]) ++i, ++j;        else j = Next[j];        if(j != -1 && !pat[j]) return true;    }    return false;}bool check(int k, int len){    if(k > len/3) return false;    for(int i = 0; i < k; i++) pat[i] = ori[i];    pat[k] = '\0';    char ch ='\0';    swap(ch, ori[len-k+1]);    bool ans = kmp(ori + k, pat);    swap(ch, ori[len-k+1]);    return ans;}int main(){    scanf("%s", ori);    get_next(ori);    int len = strlen(ori);    int k = Next[len];    int ans = 0;    while(true)    {        if(check(k, len))        {            ans = k; break;        }        else        {            k = Next[k];            if(k <= 0) break;        }    }    printf("%d\n", ans);    return 0;}