zoukankan      html  css  js  c++  java
  • [POI2012]OKR-A Horrible Poem hash

    题面: 洛谷

    题解:

      首先我们需要知道一个性质,串s的最小循环节 = len - next[len].其中next[len]表示串s的一个最长长度使得s[1] ~ s[next[len]] == s[len - next[len] + 1] ~ s[len](详细定义参见KMP)

      至于为什么是成立的可以画图推一下,这个应该是比较常见的性质。

      

      可能画的有点丑。。。

      图中每个绿色方块所代表的串都是相同的,因为第一个绿块显然与下面那块相同,而根据next的定义,它也与第二行第二个绿块相同……以此类推,可以一直递推下去,直到推完整个数组。

      

      对于一个长度为len的串s而言,设它的最小循环节长度为l.

      若$len = p_{1}^{k_{1}} cdot p_{2}^{k_{2}} cdot p_{3}^{k_{3}}...p_{t}^{k_{t}}$

      则$len = p_{1}^{a_{1}} cdot p_{2}^{a_{2}} cdot p_{3}^{a_{3}}...p_{t}^{a_{t}}$其中$a_{i} le k_{i}$.

      首先一个串的最大循环节长度肯定= len;

      而这个循环节之所以可以变小,是因为这个最大循环节是由很多个最短循环节组成。我们假设最短循环节为X,这这个串可以表示为XXXXXXX(若干个X)

      假设X有b个。那么我们可以每次对b缩减一个b的因子,最后使得b变为1,即使b不断除一个数。

      例如一个串s一开始可以被表示为XXXXXXXX(8个X),即b = 8

      这个时候我们枚举到一个2,于是我们判断原串是否可以被XXXX + XXXX凑出,如果可以,那么b /= 2.

      然后我们判断原串是否可以被XX + XX + XX + XX凑出,如果可以,那么b /= 2.

      依次类推,直到已经没有更小的循环节可以凑出原串位置。

      其中2是b的某个质因子。

      因为b的最大值为len(即循环节为1),所以我们一开始先从len开始,不断枚举len的质因子,看能否消去这个因子,最后剩下的数就是答案。

      判断一个长度是否可以成立,可以用hash判断图中红色部分是否相等

      

      其中绿块长度为x。

      原理就是一开始解释过的KMP求最小循环节。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define R register int
     4 #define AC 501000
     5 #define p1 1000000007
     6 #define p2 998244353
     7 #define base 26
     8 #define LL long long
     9 
    10 int n, m, tot, top;
    11 int hash1[AC], hash2[AC], pw1[AC], pw2[AC];
    12 int pri[AC], last[AC], q[AC];
    13 char s[AC];
    14 bool z[AC];
    15 
    16 inline int read()
    17 {
    18     int x = 0;char c = getchar();
    19     while(c > '9' || c < '0') c = getchar();
    20     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    21     return x;
    22 }
    23 
    24 void get()//欧拉筛
    25 {
    26     for(R i = 2; i <= n; i ++)
    27     {
    28         if(!z[i]) pri[++ tot] = i, last[i] = i;
    29         for(R j = 1; j <= tot; j ++)
    30         {
    31             int now = pri[j];
    32             if(now * i > n) break;
    33             z[now * i] = true, last[now * i] = now;
    34             if(!(i % now)) break;
    35         }
    36     }
    37 }
    38 
    39 void pre()
    40 {
    41     n = read();
    42     scanf("%s", s + 1);
    43 }
    44 
    45 void build()//求前缀hash值
    46 {
    47     pw1[0] = pw2[0] = 1;
    48     for(R i = 1; i <= n; i ++) 
    49     {
    50         hash1[i] = (1ll * hash1[i - 1] * base + s[i] - 'a' + 1) % p1;
    51         hash2[i] = (1ll * hash2[i - 1] * base + s[i] - 'a' + 1) % p2;
    52         pw1[i] = 1ll * pw1[i - 1] * base % p1;//存下base的i次方
    53         pw2[i] = 1ll * pw2[i - 1] * base % p2;
    54     }
    55 }
    56 
    57 LL cal(int l, int r, bool w){
    58     if(!w) return ((1ll * hash1[r] - 1ll * hash1[l - 1] * pw1[r - l + 1] % p1) + p1) % p1;
    59     else return ((hash2[r] - 1ll * hash2[l - 1] * pw2[r - l + 1] % p2) + p2) % p2;
    60 }
    61 
    62 bool check(int l, int r, int x){//测试区间[l, r]的循环结是否可能为x
    63     return (cal(l + x, r, 0) == cal(l, r - x, 0)) && (cal(l + x, r, 1) == cal(l, r - x, 1));
    64 }
    65 
    66 void work()
    67 {
    68     m = read();
    69     for(R i = 1; i <= m; i ++)
    70     {
    71         int l = read(), r = read(), x = r - l + 1;
    72         top = 0;
    73         while(x != 1) q[++ top] = last[x], x /= last[x];
    74         x = r - l + 1;
    75         for(R i = 1; i <= top; i ++) 
    76             if(check(l, r, x / q[i])) x /= q[i];    
    77         printf("%d
    ", x);
    78     }
    79 }
    80 
    81 int main()
    82 {
    83 //    freopen("in.in", "r", stdin);
    84     pre();
    85     get();//处理last数组
    86     build();//构建hash数组
    87     work();
    88 //    fclose(stdin);
    89     return 0;
    90 }
    View Code

      

  • 相关阅读:
    微信 token ticket jsapi_ticket access_token 获取 getAccessToken get_jsapi_ticket方法
    PHP 日志 记录 函数 支持 数组 对象 新浪 sae 环境 去掉 空格 换行 格式化 输出 数组转字符串
    原生 原始 PHP连接MySQL 代码 参考mysqli pdo
    PHP 数字金额转换成中文大写金额的函数 数字转中文
    使用PHPMailer发送带附件并支持HTML内容的邮件
    设置输出编码格式 header 重定向 执行时间 set_time_limit 错误 报告 级别 error_reporting
    html5 bootstrap pannel table 协议 公告 声明 文书 模板
    指向指针的指针
    二级指针
    c语言:当指针成为参数后
  • 原文地址:https://www.cnblogs.com/ww3113306/p/10066983.html
Copyright © 2011-2022 走看看