P5404-[CTS2019]重复【KMP,dp】

1|0正题

题目链接:https://www.luogu.com.cn/problem/P5404


1|1题目大意

给出一个字符串S,然后求有多少个长度为m的串T满足。无限多个串T拼接起来后能找出一个长度和S相等的子串字典序比S小。

1|S|,m2000


1|2解题思路

首先有一个小于的很难找,所以我们找有多少一直大于等于的减去就好了。

然后其实如果有一个大于位置大于S串匹配就可以直接不管,所以其实我们主要考虑前面都相等的情况,(根据题解)考虑用KMP

设我们现在匹配到[1,k],然后有[1,nxtk]=[knxtk+1,k],然后加了一个字符如果有跳的边而且是转移边里面字符最大的,因为我们显然需要匹配出一个最大的前缀不然不能保证有小于的时候能直接找到。

而且如果我们现在在KMP上走了T之后节点是i,那么TT也是会匹配回到节点i的,所以相当于我们要找一个节点p使得它匹配了T之后仍然是回到节点p

暴力枚举节点来dp肯定是会T,考虑优化一下。

不难发现如果一个点走m步之后没有回到过0号节点的话方案只有一种(因为每个点连接0以外的出边最多只有一条)。

所以设fi,j表示从0出发走j步到达i的方案数。

然后对于起点枚举多少步后走到0再用f统计答案就好了。

时间复杂度O(nm)


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const ll N=2100,P=998244353; ll n,m,ans,nxt[N],ch[N][26],f[N][N],mx[N]; char s[N]; signed main() { scanf("%lld%s",&m,s+1); n=strlen(s+1);ans=1; for(ll i=1;i<=m;i++)ans=ans*26ll%P; for(ll i=2,j=0;i<=n;i++){ while(j&&s[i]!=s[j+1])j=nxt[j]; j+=(s[i]==s[j+1]);nxt[i]=j; } for(ll i=0;i<=n;i++) for(ll c=0;c<26;c++){ if(s[i+1]==c+'a')ch[i][c]=i+1; else ch[i][c]=ch[nxt[i]][c]; if(ch[i][c])mx[i]=c; } f[0][0]=1; for(ll i=0;i<m;i++) for(ll j=0;j<=n;j++) for(ll c=mx[j];c<26;c++) (f[ch[j][c]][i+1]+=f[j][i])%=P; for(ll i=0;i<=n;i++){ ll x=i; for(ll j=1;j<=m;j++){ (ans-=(25-mx[x])*f[i][m-j]%P)%=P; x=ch[x][mx[x]]; if(!x)break; } if(i&&x==i)(ans+=P-1)%=P; } printf("%lld\n",(ans+P)%P); return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/14978300.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(58)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示