• A Horrible Poem


    A Horrible Poem

    题目:

    给出一个由小写英文字母组成的字符串S,再给出q个询问,要求回答S某个子串的最短循环节
    n≤500 000 表示S的长度。
    q≤2 000 000 表示询问次数。

    code:

    /*
      Time: 1.10
      Worker: Blank_space
      Source: #10038. 「一本通 2.1 练习 4」A Horrible Poem
    */
    /*---------------------------------------------------*/
    #include<cstdio>
    using namespace std;
    /*---------------------------------------------------*/
    const int base = 60;
    const int A = 1e4 + 7;
    const int B = 1e5 + 7;
    const int C = 1e6 + 7;
    const int D = 1e7 + 7;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    /*---------------------------------------------------*/
    long long n, q, h[B << 3], pow[B << 3], prim[B << 3], cnt, c[B << 3], ans;
    bool p, vis[B << 3];
    char s[B << 3];
    /*---------------------------------------------------*/
    int read()
    {
    	int x = 0, f = 1; char ch = getchar();
    	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
    	return x * f;
    }
    void pair()
    {
    	for(int i = 2; i <= n; i++)	if(!vis[i])
    		for(int j = 1; j * i <= n; j++)
    			if(!vis[i * j]) vis[i * j] = 1, prim[i * j] = i;
    }
    bool check(int l, int r, int x)
    {
    	if((h[r - x] - h[l - 1] * pow[r - l - x + 1] % mod + mod) % mod != (h[r] - h[l + x - 1] * pow[r - l - x + 1] % mod + mod) % mod) return 0;
    	return 1;
    }
    /*---------------------------------------------------*/
    int main()
    {
    //	freopen("okr2.in", "r", stdin);
    //	freopen("", "w", stdout);
    	
    	n = read(); scanf("%s", s + 1); q = read(); pow[0] = 1; pair();
    	for(int i = 1; i <= n; i++) pow[i] = pow[i - 1] * base % mod, h[i] = (h[i - 1] * base % mod + s[i] - 96) % mod;
    	for(int i = 1; i <= q; i++)
    	{
    		int l = read(), r = read();
    		int len = ans = r - l + 1; p = 0;
    		while(len > 1)
    		{
    			int j = ans / prim[len]; len /= prim[len];
    			if(check(l, r, j)) ans = j;
    		}
    		printf("%lld
    ", ans);
    	}
    	
    //	fclose(stdin);
    //	fclose(stdout);
    }
    
    

    思路:

    首先 第一个想法是暴力枚举

    对于每一个区间 枚举一个子串看是否为循环节 判断的时候我们可以采用哈希 循环枚举每一个长度的子串

    很明显 时间复杂度起飞了

    考虑优化

    能够构成区间循环节的子串的长度一定能被整个区间的长度整除 可以省去不必要的判断

    判断的过程不需要循环比较整个串 如果一个子串是循环节 很显然的 其必然是从区间左端开始 且满足 (s_{(l, r - x)} == s_{(l + x, r)})

    这样我们将每一次的判断优化为了 (O(1))

    确实多过了几个点 到现在已经有六十分了(其实我自己写的代码参考题解之前也只有六十分)

    最后一个强力的优化

    线筛:可能被选出的循环节的长度一定是长度的质因数

    正确性:

    循环节的长度若不是区间长度的质因数
    若其是质因数的倍数 则一定能由选出的质因数构成
    否则其必然无法被整个区间的长度整除

    所以我们可以直接预处理出长度的质因数
    直接枚举质因数 判断即可

  • 相关阅读:
    Jquery才可以使用 this 指定当前DOM
    微擎使用腾讯地图拾取坐标
    使用 MUI 自制 弹出层
    mui 底部导航栏
    PHP 向数组头部插入数据
    a 标签添加 onclick 事件
    # & 等特殊字符会导致传参失败
    HTML 颜色输入框修改事件的触发,以及获取修改后的颜色
    C++ malloc()函数的注意点及使用示例
    C++ malloc函数
  • 原文地址:https://www.cnblogs.com/blank-space-/p/14258214.html
Copyright © 2020-2023  润新知