[NOI2014]动物园

刚刚又复习了一遍KMP

发现自己学了KMP之后没写过题

这道题O(n)的思路还是比较巧妙的

但是我用nlogn的暴力给整过去了

考虑暴力

找到\(pos(i<pos<=i)使得S[1~k]和S[pos-k+1,pos]相同\)

那么就可以利用\(next[i]\)表示从1~i前缀等于后缀的长度的性质

从i一直跳next直到再跳就跳到<i的位置位置

\(num[i]\)就是跳的次数

这个复杂度是O(n^2)的

但是显然可以用倍增优化跳Nxt

所以时间复杂度nlogn

#include<cstdio>
#include<cstring>
#include<algorithm>
# define LL long long
const int M = 1000005 ;
const int mod = 1e9 + 7 ;
using namespace std ;
char st[M] ;
int n , now , Nxt[20][M] , Num[M] , lg[M] ;
LL Ans ;
int main() {
	int T ; scanf("%d",&T) ;
	for(int i = 2 ; i <= 1000000 ; i ++) lg[i] = lg[i >> 1] + 1 ;
	while(T--) {
		Ans = 1 ; now = 0 ; 
		scanf("%s",st) ; n = strlen(st) ;
		for(int i = 1 ; i < n ; i ++) {
			while(now && st[i] != st[now]) now = Nxt[0][now] ;
			Nxt[0][i + 1] = (st[i] == st[now] ? ++ now : 0) ;
		}
 		for(int j = 1 ; j <= lg[n] ; j ++)
 		    for(int i = 1 ; i <= n ; i ++)
 		        Nxt[j][i] = Nxt[j - 1][Nxt[j - 1][i]] ;
		for(int i = 2 , tot , x ; i <= n ; i ++) {
			x = i ; tot = 0 ;
			for(int j = lg[i] ; j >= 0 ; j --)
			    if((Nxt[j][x] << 1) > i)
			        x = Nxt[j][x] ;
			for(int j = lg[i] ; j >= 0 ; j --)
			    if(Nxt[j][x])
			    	x = Nxt[j][x] , tot += (1 << j) ;
			Ans = (Ans * (tot + 1)) % mod ;
		}
		printf("%lld\n",Ans) ;
	}
	return 0 ;
}

然而倍增必须要有优秀的常数才能通过

所以正解是O(n)的

我们考虑前后缀可以重叠

那么可以用val[i]表示重叠的答案

这个显然可以O(n)递推出来

\(Num[i]\)就是\(val[pos]\)

\(pos\)满足\(<=i/2\)之前且\(S[i]==S[pos]\)

但是然后我们就用KMP匹配目标串的方式再对原串匹配一遍

更新答案就可以了

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define LL long long
const int M = 1000005 ;
const int mod = 1e9 + 7 ;
using namespace std ;
int n , Nxt[M] , now , val[M] ;
char st[M] ;
LL Ans ;
int main() {
	int T ; scanf("%d",&T) ;
	while(T --) {
		now = 0 ; Ans = 1 ;
		scanf("%s",st) ; n = strlen(st) ;
		for(int i = 1 ; i < n ; i ++) {
			while(now && st[i] != st[now]) now = Nxt[now] ;
			Nxt[i + 1] = (st[i] == st[now] ? ++ now : 0) ;
		}
		for(int i = 1 ; i <= n ; i ++) val[i] = val[Nxt[i]] + 1 ;
		now = 0 ;
		for(int i = 1 ; i < n ; i ++) {
			while(now && st[now] != st[i]) now = Nxt[now] ;
			now += (st[i] == st[now]) ;
			while((now << 1) > i + 1) now = Nxt[now] ;
			Ans = (Ans * (LL)(val[now] + 1)) % mod ;
		}
		printf("%lld\n",Ans) ;
	}
}
posted @ 2018-10-11 21:53  beretty  阅读(140)  评论(0编辑  收藏  举报