zoukankan      html  css  js  c++  java
  • 洛谷P3435 [POI2006]OKR-Periods of Words

    题目

    传送门

    这题意不是一般人能读懂的,为了读懂题目,我还特意去翻了题解[手动笑哭]

    题目大意:

    给定一个字符串s

    对于(s)的每一个前缀子串(s1),规定一个字符串(Q),(Q)满足:(Q)(s1)的前缀子串且Q不等于(s1)(s1)是字符串(Q+Q)的前缀.设(siz)为所有满足条件的(Q)(Q)的最大长度(注意这里仅仅针对(s1)而不是(s),即一个(siz)的值对应一个(s1))

    求出所有(siz)的和

    不要被这句话误导了:

    求给定字符串所有前缀的最大周期长度之和

    正确断句:求给定字符串 所有/前缀的最大周期长度/之和

    我就想了半天:既然是"最大周期长度",那不是唯一的吗?为什么还要求和呢?

    思路

    其实这题要AC并不难(看通过率就知道)

    看图

    要满足(Q)(s1)的前缀,则(Q)(1)~(5)位和(s1)的1~5位是一样的,又因为(s1)(Q+Q)的前缀,所以又要满足(s1)的6~8位和(Q+Q)的6~8位一样,即(s1)的6~8位和Q的1~3位相等,回到(s1),标蓝色的两个位置相等.

    回顾下KMP中(next)数组的定义:next[i]表示对于某个字符串a,"a中长度为next[i]的前缀子串"与"a中以第i为结尾,长度为next[i]的非前缀子串"相等,且next[i]取最大值

    是不是悟到了什么,是不是感觉这题和(next)数组冥冥之中有某种相似之处?

    但是,这仅仅只是开始

    按照题目的意思,我们要让(Q)的长度最大,也就是图中蓝色部分长度最小,但是(next)中存的是蓝色部分的最大值,显然,两者相违背,难道我们要改造(next)数组吗?明显不行,若(next)存储的改为最小值,则原来求(next)的方法行不通.考虑换一种思路(一定要对KMP中(next)的求法理解透彻,不然下面看不懂,不行的复习一下),我们知道对于next[i],next[next[i-1]],next[next[next[i]]]...都能满足"前缀等于以(i)结尾的子串"这个条件,且越往后,值越小,所以,我们的目标就定在上面序列中从后往前第一个不为0的(next)

    极端条件下,暴力跑可以去到(O(n^2)),理论上会超时(我没试过)

    两种优化:

    1. 记忆化,时间效率应该是O(n)这里不详细讲,可以去到洛谷题解查看
    2. 倍增(我第一时间想到并AC的做法):
      我们将j=next[j]这一语句称作"j跳了一次"(感觉怪怪的),将next拓展为2维,next[i][k]表示结尾为i,j跳了2^k的前缀字符长度(也就是next[i][0]等价于原来的next[i])
      借助倍增LCA的思想(没学没关系,现学现用),这里不做赘述,上代码
    		int tmp = i;
    		for(rr int j = siz[i] ; j >= 0 ; --j)//siz[i]是next[i][j]中第一个为0的小标j,注意倒序枚举
    			if(next[tmp][j] != 0)//如果不为0则跳
    				tmp = next[tmp][j];
    

    倍增方法在字符串长度去到(10^6)时是非常危险的,带个(log)理论是(2cdot 10^7)左右,常数再大那么一丢丢就TLE了,还好数据比较水,但是作为倍增和KMP的练习做一下也是不错的

    最后,记得开longlong(不然我就一次AC了)

    完整代码

    #include <iostream>
    #include <cmath>
    #include <cstdio>
    #define nn 1000010
    #define rr register
    #define ll long long
    using namespace std;
    int next[nn][30] ;
    int siz[nn];
    char s[nn];
    int n;
    int main() {
    //	freopen("P3435_3.in" , "r" , stdin);
    	cin >> n;
    	do
    		s[1] = getchar();
    	while(s[1] < 'a' || s[1] > 'z');
    	for(rr int i = 2 ; i <= n ; i++)
    		s[i] = getchar();
    	
    	next[1][0] = 0;
    	for(rr int i = 2 , j = 0 ; i <= n ; i++) {
    		while(j != 0 && s[i] != s[j + 1])
    			j = next[j][0];
    		if(s[j + 1] == s[i])
    			++j;
    		next[i][0] = j;
    	}
    	
    	rr int k = log(n) / log(2) + 1;
    	for(rr int j = 1 ; j <= k ; j++)
    		for(rr int i = 1 ; i <= n ; i++) {
    			next[i][j] = next[next[i][j - 1]][j - 1];
    			if(next[i][j] == 0)
    				siz[i] = j;
    		}
    	ll ans = 0;
    	for(rr int i = 1 ; i <= n ; ++i) {
    		int tmp = i;
    		for(rr int j = siz[i] ; j >= 0 ; --j)
    			if(next[tmp][j] != 0)
    				tmp = next[tmp][j];
    		if(2 * (i - tmp) >= i && tmp != i)
    			ans += (ll)i - tmp;
    	}
    	cout << ans;
    	return 0;
    } 
    
  • 相关阅读:
    DEDECMS里面DEDE函数解析
    dede数据库类使用方法 $dsql
    DEDE数据库语句 DEDESQL命令批量替换 SQL执行语句
    织梦DedeCms网站更换域名后文章图片路径批量修改
    DSP using MATLAB 示例 Example3.12
    DSP using MATLAB 示例 Example3.11
    DSP using MATLAB 示例 Example3.10
    DSP using MATLAB 示例Example3.9
    DSP using MATLAB 示例Example3.8
    DSP using MATLAB 示例Example3.7
  • 原文地址:https://www.cnblogs.com/dream1024/p/13971779.html
Copyright © 2011-2022 走看看