• [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
    ",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
    ",Ans) ;
    	}
    }
    
  • 相关阅读:
    Luogu P6623 [省选联考 2020 A 卷] 树|Trie
    Luogu P4683【IOI2008】Type Printer 打印机|trie
    Luogu P5658 括号树|搜索+递推
    Luogu P4514 上帝造题的七分钟|二维树状数组
    Luogu P1314 【NOIP2011】聪明的质检员|前缀和+二分
    Html5表单元素
    HTML5视频音频
    HTML5语义化标签
    斗地主案例
    Collection集合
  • 原文地址:https://www.cnblogs.com/beretty/p/9775323.html
Copyright © 2020-2023  润新知