Luogu P8112 [Cnoi2021] 符文破译 题解 [ 蓝 ] [ KMP ] [ 线性 dp ] [ 决策单调性 dp ]
符文破译:KMP + dp 的好题。
暴力 dp
不难打出一个暴力 dp:设计
转移也是简单的,我们在 KMP 的过程中进行 dp 转移,每次选取 next 不断跳向再前面的 next,然后进行转移即可。
很显然一个字符集大小为
决策单调性
我们观察每个 dp 值是如何做决策的,先是走到最长的匹配位置,然后匹配的长度不断减小。
同时,根据贪心思想,前面的 dp 值一定不比后面的 dp 值大。为啥呢,可以用反证法。假设前面的 dp 值比后面的 dp 值大,那么分割数必定比后面的多。如果后面的都能分割,又因为每一段都是模式串的前缀,所以一个成功分割的字符串无论你从后面删多少个数它都是合法的分割,这样前面的 dp 值一定能从后面的 dp 值的分割方案中求出。因此前面的 dp 值一定不大于后面的。
于是只要转移最长的 next 就可以了。
其他的注意一下无解判断即可。
时间复杂度
代码
#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pi;
int n,m,dp[10000005],ne[10000005];
char s[10000005],t[10000005];
void solve()
{
memset(dp,-1,sizeof(dp));
dp[0]=0;
cin>>n>>m>>s+1>>t+1;
for(int i=2,j=0;i<=n;i++)
{
while(j&&s[j+1]!=s[i])j=ne[j];
if(s[j+1]==s[i])j++;
ne[i]=j;
}
for(int i=1,j=0;i<=m;i++)
{
while(j&&s[j+1]!=t[i])j=ne[j];
if(s[j+1]==t[i])j++;
if(dp[i-j]!=-1)dp[i]=dp[i-j]+1;
}
if(dp[m]!=-1)cout<<dp[m];
else cout<<"Fake";
}
int main()
{
//freopen("sample.in","r",stdin);
//freopen("sample.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战