hdu4333——拓展kmp

来源:互联网 发布:java 干10年工资多少 编辑:程序博客网 时间:2024/05/01 19:05

题意:给定一个数字序列,不断把后缀移到前面去,组成新的数字序列。问分别由多少个不同的数比原数字小、比原数字大、和原数字相等。

很容易想到拓展kmp的:copy一次原串放后面,对新串求一次extend数组,然后遍历一遍extend数组:extend[i] >= n 说明相等;否则比较str[extend[i]]和str[i + extend[i]]确定与原数的比较结果。

难处理的是怎样把相同的数过滤掉,开始想到了用后缀数组:对height[i]  >= n的后缀分组标记出相同的后缀。但还是没写,太繁琐了。后来听说这题卡时限很紧,后缀数组过不了。其实在做kmp的题的时候,应该碰到过这种情况:只有原串中存在循环节,才会遇到上述情况。充分性很好证明,必要性用反证法可以证明。判断循环节的方法很简单,找到第一个i使得i + extend[i] >= n,在判断n % i是否等于零,如果等于零,遍历extend数组的时候到i就可以停止了。

#include <iostream>#include <cstring>#include <cstdio>#include <algorithm>using namespace std;const int maxn = 200000 + 10;char str[maxn];int extend[maxn];int n;void get_extend(){    extend[0] = 2 * n;    for(int i = 1, q = -1, a, p; i < 2 * n; ++i, --q)    {       if(q < 0 || extend[i - a] >= p - i)       {           if(q < 0) q = 0, p = i;           while(p < 2 * n && str[p] == str[q]) p++, q++;           extend[i] = q, a = i;       }       else extend[i] = extend[i - a];    }}int main(){    freopen("in.txt", "r", stdin);    int t, cc= 1;    scanf("%d", &t);    while(t--)    {        scanf("%s", str); n = strlen(str);        for(int i = 0; i < n; ++i) str[n + i] = str[i];        str[2 * n] = '\0';        get_extend();        int m;        for(m = 1; m < n && m + extend[m] < n; ++m);        int m1 = (n % m == 0) ? m : n;        int l = 0, e = 1, g = 0;        for(int i = 1; i < m1; ++i)        {            int tem = extend[i];            if(tem >= n) e++;            else            {                if(str[i + tem] < str[tem]) l++;                else g++;            }        }        printf("Case %d: %d %d %d\n", cc++, l, e, g);    }    return 0;}/*1341Case 1: 1 1 1*/