[NOI2014]动物园 题解(预览)

一开始读错题了淦。


对于一个位置 (i)(num[i]) 显然是满足条件的最大前缀长 (k)(next[k]) 跳到 (0) 需要的次数。

对每个 (i) 如何求 (k)? 类比 (next) 的递推方式, 只需要加个限制长度的 (while) 语句就可以求出 (k) 了。((i)(k)(i-1)(k) 推出即可,至于为什么,画一画qwq)

如何计数一个位置 (s)(nxt) 跳到 (0) 的次数?可以发现若对于每个 (s),将 (nxt[s])(s) 连一条有向边, 所形成的结构就是一棵树, 其中, 入度为 (0) 的节点为 (0), 且 (s)(nxt) 跳到 (0) 需要跳的次数就是以 (0) 为根时 (s) 的深度。


luogu数据AC代码

#include<bits/stdc++.h>
using namespace std;
#define li long long
const int maxn = 1e6+5;
const int mod = 1000000007;

int len, nxx[maxn], nxt[maxn];
char s[maxn];

int ct, hed[maxn], nx[maxn<<1], ver[maxn<<1];
void ad(int a,int b) {
	ver[++ct] = b;
	nx[ct] = hed[a];
	hed[a] = ct;
}
int dep[maxn];
void dfs(int x,int Dep) {
	dep[x]=Dep;
	for(int i=hed[x];i;i=nx[i]) {
		int y=ver[i];
		dfs(y,Dep+1);
	}
}

int main()
{
	int n; cin>>n; while(n--) {
		ct = 0;
		memset(hed,0,sizeof hed);
		scanf("%s", s+1); len=strlen(s+1);
		nxt[1]=0;
		for(int i=2,j=0;i<=len;++i) {
			while(j&&s[j+1]!=s[i]) j=nxt[j];
			if(s[j+1]==s[i]) ++j;
			nxt[i]=j;
		}
		nxx[1]=0;
		for(int i=2,j=0;i<=len;++i) {
			while(j+1>i/2) j=nxt[j];
			while(j&&s[j+1]!=s[i]) j=nxt[j];
			if(s[j+1]==s[i]) ++j;
			nxx[i]=j;
		}
		//
		for(int i=1;i<=len;++i) ad(nxt[i],i);
		dfs(0,1);
		li ans = 1ll;
		for(int i=1;i<=len;++i) ans = ans*1ll*dep[nxx[i]]%mod;
		cout<<ans<<'
';
	}
	return 0;
}
原文地址:https://www.cnblogs.com/tztqwq/p/12799722.html