题目描述

  给你一个字符串\(s\),问你有多少个串是最小表示串且字典序\(\leq s\)

  \(|s|\leq 1000\)

题解

  先把\(s\)变成比\(s\)小的最大的最小表示串。方法是从后枚举每一个字符,如果这个字符不是'a',就把这个字符变成这个字符的前驱,并把后面所有字符字符变成'z',然后判断是不是最小表示串。

  可以用kmp去判断。如果\(\exists i,s_{i+1}>s_{fail_i+1}\),那么这个串就不是最小表示串。

  运用polya定理,把问题转化为求有多少个长度为\(i\)的字符串的最小表示串\(<s\)。最后把答案加上\(1\)。

  如果一个字符串的最小表示串字典序比\(s\)小,那么就在这个串左旋到第一次字典序小于\(s\)的时候统计。

  有两种情况:

  情况1:\(???s_1s_2\ldots s_mr????\)

  情况2:\(s_{k+1}\ldots s_mr????s_1s_2\ldots s_k\)

  这两种情况都要求\(r<s_{m+1}\)

  先看第一种情况。

  枚举前面\(?\)的数量和\(m\),要\(O(n^2)\)的时间。

  后面的\(?\)可以任意取,但前面的不行。

  考虑暴力枚举所有情况,然后拿\(s\)去和这个串做KMP。

  如果前面匹配时有对应位置比\(s_i\)小的情况,那么显然是不合法的。

  如果前面的\(?\)匹配完后\(s\)中的匹配长度不为\(0\),那么\(s_1s_2\ldots s_m\)就不是第一次匹配了。(因为\(s\)是最小表示串,所以它的后缀一定大于整个串。)

  设\(f_{i,j}\)为有\(i\)个\(?\),从\(s_j\)开始匹配,匹配完后匹配长度为\(0\)的方案数。

  如果这一位匹配,那么方案数就是\(f_{i-1,j+1}\)

  如果不匹配,那么这一位肯定比\(s_j\)大,那就是\(('z'-s_j)f_{i-1,1}\)

\[f_{i,j}=f_{i-1,j+1}+('z'-s_j)f_{i-1,1}
\]

  这样第一部分就做完了。

  接下来看第二种情况。

  枚举\(k,m\),拿\(s\)去和\(s_{k+1}\ldots s_m\)跑KMP。

  假设当前匹配长度为\(l\)。

  但是这次在匹配完后有两种情况。

  如果在\(r\)处匹配上了,方案数就是后面\(?\)部分的方案数。

  如果失配了,那么有\(s_{l+1}<r<s_{m+1}\)(如果\(r<s_{l+1}\),那么左旋\(k-l\)次就比\(s\)小了)。

  第二部分也做完了。

  做一次的时间复杂度是\(O(n^2)\)

  但是要做很多次,时间复杂度还是\(O(n^2)\)的。

  还有一点细节。如果\(s="abacab"\),当要求的字符串的循环节长度为\(2\)的时候,后面有一部分\(s_{3\ldots 4}\)比第一部分\(s_{1\ldots 2}\)大,那么第一部分的所有循环同构串都是合法的。

代码

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<algorithm>
  4. using namespace std;
  5. typedef long long ll;
  6. const ll p=1000000007;
  7. ll fp(ll a,ll b)
  8. {
  9. ll s=1;
  10. for(;b;b>>=1,a=a*a%p)
  11. if(b&1)
  12. s=s*a%p;
  13. return s;
  14. }
  15. int gcd(int a,int b)
  16. {
  17. return b?gcd(b,a%b):a;
  18. }
  19. char s[1010];
  20. int a[2010];
  21. int n;
  22. ll f[1010][1010];
  23. int fail[2010];
  24. ll pw[1010];
  25. ll g[1010];
  26. int chk(int x)
  27. {
  28. int i;
  29. for(i=x+1;i<=n;i++)
  30. if(a[i]>a[(i-1)%x+1])
  31. return 1;
  32. return 0;
  33. }
  34. ll gao(int x)
  35. {
  36. ll ans=0;
  37. for(int i=0;i<x;i++)
  38. for(int j=1;j<=x-i;j++)
  39. ans=(ans+f[i][1]*(a[j]-1)%p*pw[x-i-j])%p;
  40. for(int i=1;i<x;i++)
  41. {
  42. int k=0;
  43. for(int j=0;j<=x-1-i;j++)
  44. {
  45. for(;k&&a[j+i]!=a[k+1];k=fail[k]);
  46. if(j&&a[j+i]==a[k+1])
  47. k++;
  48. if(a[k+1]<a[j+i+1]-1)
  49. ans=(ans+(a[j+i+1]-a[k+1]-1)*f[x-1-i-j][1])%p;
  50. if(a[k+1]<=a[j+i+1]-1)
  51. ans=(ans+f[x-1-i-j][k+2])%p;
  52. }
  53. }
  54. return ans;
  55. }
  56. int check()
  57. {
  58. for(int i=1;i<=n;i++)
  59. a[i+n]=a[i];
  60. fail[1]=0;
  61. int j=0;
  62. for(int i=2;i<=2*n;i++)
  63. {
  64. while(j&&a[j+1]!=a[i])
  65. j=fail[j];
  66. if(a[j+1]==a[i])
  67. j++;
  68. fail[i]=j;
  69. }
  70. for(int i=1;i<2*n;i++)
  71. if(a[i+1]<a[fail[i]+1])
  72. return 0;
  73. return 1;
  74. }
  75. void solve()
  76. {
  77. scanf("%s",s+1);
  78. n=strlen(s+1);
  79. for(int i=1;i<=n;i++)
  80. a[i]=s[i]-'a'+1;
  81. pw[0]=1;
  82. for(int i=1;i<=n;i++)
  83. pw[i]=pw[i-1]*26%p;
  84. for(int i=n;i>=1;i--)
  85. {
  86. if(i!=n&&a[i]>1)
  87. a[i]--;
  88. if(check())
  89. break;
  90. a[i]=26;
  91. }
  92. memset(f,0,sizeof f);
  93. f[0][1]=1;
  94. for(int i=1;i<=n;i++)
  95. for(int j=1;j<=n;j++)
  96. {
  97. f[i][j]=f[i-1][1]*(26-a[j])%p;
  98. f[i][j]=(f[i][j]+f[i-1][j+1])%p;
  99. }
  100. for(int i=1;i<=n;i++)
  101. if(n%i==0)
  102. {
  103. g[i]=gao(i);
  104. if(chk(i))
  105. {
  106. if(i%(i-fail[i])==0)
  107. g[i]+=i-fail[i];
  108. else
  109. g[i]+=i;
  110. }
  111. g[i]%=p;
  112. }
  113. ll ans=0;
  114. for(int i=1;i<=n;i++)
  115. ans=(ans+g[gcd(i,n)])%p;
  116. ans=ans*fp(n,p-2)%p;
  117. ans++;
  118. ans=(ans+p)%p;
  119. printf("%lld\n",ans);
  120. }
  121. int main()
  122. {
  123. #ifndef ONLINE_JUDGE
  124. freopen("string.in","r",stdin);
  125. freopen("string.out","w",stdout);
  126. #endif
  127. int t;
  128. scanf("%d",&t);
  129. while(t--)
  130. solve();
  131. return 0;
  132. }

【XSY2779】最小表示串 KMP DP polya定理的更多相关文章

  1. HihoCoder - 1807:好的数字串 (KMP DP)

    Sample Input 6 1212 Sample Output 298 给定一个数字字符串S,如果一个数字字符串(只包含0-9,可以有前导0)中出现且只出现1次S,我们就称这个字符串是好的. 例如 ...

  2. 2021.11.09 P3426 [POI2005]SZA-Template(KMP+DP)

    2021.11.09 P3426 [POI2005]SZA-Template(KMP+DP) https://www.luogu.com.cn/problem/P3426 题意: 你打算在纸上印一串字 ...

  3. 【转】Polya定理

    转自:http://endlesscount.blog.163.com/blog/static/82119787201221324524202/ Polya定理 首先记Sn为有前n个正整数组成的集合, ...

  4. polya定理小结

    polya的精髓就在与对循环节的寻找,其中常遇到的问题就是项链染色类问题. 当项链旋转时有n种置换,循环节的个数分别是gcd(n, i); 当项链翻转时有n种置换,其中当项链珠子数位奇数时,循环节的个 ...

  5. UVA10294 Arif in Dhaka (群论,Polya定理)

    UVA10294 Arif in Dhaka (群论,Polya定理) 题意 : 给你一个长为\(n\)的项链和手镯,每个珠子有\(m\)种颜色. 两个手镯定义为相同,即它们通过翻转和旋转得到一样的手 ...

  6. 洛谷P3307 [SDOI2013]项链 [polya定理,莫比乌斯反演]

    传送门 思路 很明显的一个思路:先搞出有多少种珠子,再求有多少种项链. 珠子 考虑这个式子: \[ S3=\sum_{i=1}^a \sum_{j=1}^a\sum_{k=1}^a [\gcd(i,j ...

  7. Burnside引理与Polya定理

    感觉这两个东西好鬼畜= = ,考场上出了肯定不会qwq.不过还是学一下吧用来装逼也是极好的 群的定义 与下文知识无关.. 给出一个集合$G = \{a, b, c, \dots \}$和集合上的二元运 ...

  8. 洛谷P3193 [HNOI2008]GT考试 kmp+dp

    正解:kmp+dp+矩阵优化 解题报告: 传送门! 啊刚说想做矩阵优化dp的字符串题就找到辣QwQ虽然不是AC自动机的但都差不多嘛QwQ 首先显然可以想到一个dp式?就f[i][j]:凑出i位了,在s ...

  9. 置换群和Burnside引理,Polya定理

    定义简化版: 置换,就是一个1~n的排列,是一个1~n排列对1~n的映射 置换群,所有的置换的集合. 经常会遇到求本质不同的构造,如旋转不同构,翻转交换不同构等. 不动点:一个置换中,置换后和置换前没 ...

随机推荐

  1. TCP/IP 协议 OSI七层协议

    ------------------你来自何处并不重要,重要的是你要去往何方,人生最重要的不是所站的位置,而是所去的方向.人只要不失去方向,就永远不会失去自己! day 27 # # -------- ...

  2. Linux命令(一)

    需要用Xshell连接Linux时: 先在终端输入命令:service  sshd  start(开启ssh服务) 1.netstat -tnl:查看端口状态的命令(如 查看22端口) 2.servi ...

  3. 网络七层模型及TCP、UDP,一次HTTP请求都发生了什么

    一.七层网络模型 http协议运行在应用层   二.TCP-UDP TCP.UDP协议的区别 一次Http 请求,这个过程都发生了什么 TCP 协议如何保证可靠传输 HTTP和HTTPS的区别 TCP ...

  4. Git push提交时报错Permission denied(publickey)...Please make sure you have the correct access rights and the repository exists.

    一.git push origin master 时出错 错误信息为: Permission denied(publickey). fatal: Could not read from remote ...

  5. agora入门案例

    一,下载agora的WebSDK 二,运行index.html 三,输入appID 1.找到appID 2.页面输入appID,查看效果

  6. Python3练习题 018:打印星号菱形

    Python的内置方法 str.center(width [, fillchar]) 就能轻而易举打印出来:str即是数量不等的星号,width即是最大宽度(7个空格),默认填充字符fillchar就 ...

  7. C#复习笔记(4)--C#3:革新写代码的方式(用智能的编译器来防错)

    用智能的编译器来防错 本章的主要内容: 自动实现的属性:编写由字段直接支持的简单属性, 不再显得臃肿不堪: 隐式类型的局部变量:根据初始值推断类型,简化局部变量的声明: 对象和集合初始化程序:用一个表 ...

  8. oracle一些单记录函数

    单记录函数 1.0 NVL() 作用:从两个表达式返回一个非NULL值 用法:NVL(表达式1, 表达式2) 如果表达式1的结果不为NULL,返回表达式1的结果:如果表达式1的结果为NULL,返回表达 ...

  9. 谷歌浏览器报错 Active resource loading counts reached to a per-frame

    Active resource loading counts reached to a per-frame limit while the tab is in background. Network ...

  10. 剑指offer(20)二叉搜索树与双向表

    题目: 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. 思路一:递归法 1.将左子树构造成双链表,并返回链表头节点. 2.定位至左子 ...