题目描述

  给你一个字符串\(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}\)大,那么第一部分的所有循环同构串都是合法的。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll p=1000000007;
ll fp(ll a,ll b)
{
ll s=1;
for(;b;b>>=1,a=a*a%p)
if(b&1)
s=s*a%p;
return s;
}
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
char s[1010];
int a[2010];
int n;
ll f[1010][1010];
int fail[2010];
ll pw[1010];
ll g[1010];
int chk(int x)
{
int i;
for(i=x+1;i<=n;i++)
if(a[i]>a[(i-1)%x+1])
return 1;
return 0;
}
ll gao(int x)
{
ll ans=0;
for(int i=0;i<x;i++)
for(int j=1;j<=x-i;j++)
ans=(ans+f[i][1]*(a[j]-1)%p*pw[x-i-j])%p;
for(int i=1;i<x;i++)
{
int k=0;
for(int j=0;j<=x-1-i;j++)
{
for(;k&&a[j+i]!=a[k+1];k=fail[k]);
if(j&&a[j+i]==a[k+1])
k++;
if(a[k+1]<a[j+i+1]-1)
ans=(ans+(a[j+i+1]-a[k+1]-1)*f[x-1-i-j][1])%p;
if(a[k+1]<=a[j+i+1]-1)
ans=(ans+f[x-1-i-j][k+2])%p;
}
}
return ans;
}
int check()
{
for(int i=1;i<=n;i++)
a[i+n]=a[i];
fail[1]=0;
int j=0;
for(int i=2;i<=2*n;i++)
{
while(j&&a[j+1]!=a[i])
j=fail[j];
if(a[j+1]==a[i])
j++;
fail[i]=j;
}
for(int i=1;i<2*n;i++)
if(a[i+1]<a[fail[i]+1])
return 0;
return 1;
}
void solve()
{
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;i++)
a[i]=s[i]-'a'+1;
pw[0]=1;
for(int i=1;i<=n;i++)
pw[i]=pw[i-1]*26%p;
for(int i=n;i>=1;i--)
{
if(i!=n&&a[i]>1)
a[i]--;
if(check())
break;
a[i]=26;
}
memset(f,0,sizeof f);
f[0][1]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
f[i][j]=f[i-1][1]*(26-a[j])%p;
f[i][j]=(f[i][j]+f[i-1][j+1])%p;
}
for(int i=1;i<=n;i++)
if(n%i==0)
{
g[i]=gao(i);
if(chk(i))
{
if(i%(i-fail[i])==0)
g[i]+=i-fail[i];
else
g[i]+=i;
}
g[i]%=p;
}
ll ans=0;
for(int i=1;i<=n;i++)
ans=(ans+g[gcd(i,n)])%p;
ans=ans*fp(n,p-2)%p;
ans++;
ans=(ans+p)%p;
printf("%lld\n",ans);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
#endif
int t;
scanf("%d",&t);
while(t--)
solve();
return 0;
}

【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. vue-area-linkage Vue省市区三级列表联动插件使用

    官方演示地址 // v5及之后的版本 数据依赖于area_data npm i --save vue-area-linkage area-data import Vue from 'vue'; imp ...

  2. Go源码编译安装

    参考文档1:https://www.cnblogs.com/majianguo/p/7258975.html 参考文档2:http://www.loongson.cn/news/company/456 ...

  3. PyQuery库

    '''强大又灵活的网页解析库.如果你觉得正则写起来太麻烦,又觉得BeautifulSoup语法太难记,如果你熟悉jQuery的语法,那么PyQuery就是你的绝佳选择.'''from pyquery ...

  4. 线程中的samaphore信号量及event事件

    一.信号量 samaphore: 在程序中意思为同时允许几个线程运行,比如我们去水上乐园的滑梯玩时,有四个滑梯,每一个滑梯上当没有人在中间玩滑下去时才允许上人,四个滑梯1,2,3,4,同时最多四个人, ...

  5. 帮助小白,最新版JDK的安装与环境变量配置(Win 10系统)

    学习JAVA,必须首先安装一下JDK(java development kit java开发工具包),之后再配置环境变量就可以开始使用JAVA了. 一,安装JDK 1,可以选择到官网下载最新版本的JD ...

  6. Linux系统tree工具

    当用户在linux平台中需要需要查看一个非当前目录的目录下有哪些文件和子文件时,最普通的办法就是cd该目录,然后再ls,这个操作令人不舒适,因为查看完以后,用户还需要切换到原来的目录.现在介绍一款非常 ...

  7. ansible jenkins war

    Ansible is Simple IT Automationhttps://www.ansible.com/ Ansible中文权威指南- 国内最专业的Ansible中文官方学习手册http://a ...

  8. Oracle分析函数row_number()等的使用实例

    --分析函数 --rank() over(order by) --值相同,排名相同,序号跳跃 select * from t_account select rank() over(order by u ...

  9. js实现input的赋值

    input框赋值如下所示,是一个文本框的html代码,实际开发中,要涉及到将数据库中的数据取出然后放入input框中. <input id="name1" name=&quo ...

  10. MT4下载历史数据

    这个网站只能下载2001年-当前时间前一个月的数据,还是挺全的.但是下载下来之后好像是一分钟图的,妈蛋其实我想要1小时图的EURUSD历史数据. 网站地址:http://www.fxfupan.com ...