NOI2014 动物园
KMP好题啊……
题中要求我们求出长度不超过原字符串一般的相同的前后缀的个数。其实这个用做前面几道题的思路大致猜测一下……可以发现,我们只要从这个字符串一直往它的next递归,那么我们就可以获得一系列的公共前后缀,而且只要当next的长度<=原长度的一半的时候答案即合法。(不能继续递归,否则就重复计算了)
每个点可以获取的相同的前后缀个数可以通过kmp预处理出来,我们只要让ans[i] = ans[j]+1,(其中next[i] = j),因为在i向j跳的时候相当于获取了一个长度为j的公共前后缀的贡献。
不过我们如果这样一直递归,遇到无良数据(比如200000个a),你就T飞了,因为每次递归只会减少1的长度。解决的方法是,我们在计算的时候也像KMP一样,每次不更新j的位置,使之一直呆在小于i的一半的位置(因为你肯定是要小于i的一半才合法),这样可以避免许多无用的递归,就可以在线性时间之内求解了。
看一下代码。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<set> #include<queue> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; typedef long long ll; const int M = 10005; const int N = 1000005; const ll mod = 1000000007; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } int n,nxt[N],len; char s[N]; ll cur,ans[N]; void getnext() { int j = 0; rep(i,2,len) { while(j && s[j+1] != s[i]) j = nxt[j]; if(s[j+1] == s[i]) j++; nxt[i] = j,ans[i] = ans[j] + 1; } } void clear() { memset(nxt,0,sizeof(nxt)); memset(ans,0,sizeof(ans)); cur = 1; } int main() { n = read(); while(n--) { clear(); scanf("%s",s+1),len = strlen(s+1); ans[1] = 1,getnext(); int j = 0; //rep(i,0,len-1) printf("%d ",nxt[i]);enter; //rep(i,0,len-1) printf("%lld ",ans[i]);enter; rep(i,2,len) { while(j && s[i] != s[j+1]) j = nxt[j]; if(s[j+1] == s[i]) j++; while(j << 1 > i) j = nxt[j]; // printf("!%d\n",j); cur *= (ans[j] + 1),cur %= mod; } printf("%lld\n",cur); } return 0; }
当你意识到,每个上一秒都成为永恒。