首页 > 其他分享 >Luogu P8112 [Cnoi2021] 符文破译 题解 [ 蓝 ] [ KMP ] [ 线性 dp ] [ 决策单调性 dp ]

Luogu P8112 [Cnoi2021] 符文破译 题解 [ 蓝 ] [ KMP ] [ 线性 dp ] [ 决策单调性 dp ]

时间:2024-12-20 23:10:29浏览次数:10  
标签:分割 10000005 题解 后面 符文 KMP dp

符文破译:KMP + dp 的好题。

暴力 dp

不难打出一个暴力 dp:设计 \(dp_i\) 表示当前前 \(i\) 位全部完成了匹配,所需的最小分割数。

转移也是简单的,我们在 KMP 的过程中进行 dp 转移,每次选取 next 不断跳向再前面的 next,然后进行转移即可。

很显然一个字符集大小为 \(1\) 的串就能轻松卡掉这个,因为 KMP 的复杂度是基于均摊的,这个不满足均摊性质。时间是 \(O(n^2)\) 的。

决策单调性

我们观察每个 dp 值是如何做决策的,先是走到最长的匹配位置,然后匹配的长度不断减小。

同时,根据贪心思想,前面的 dp 值一定不比后面的 dp 值大。为啥呢,可以用反证法。假设前面的 dp 值比后面的 dp 值大,那么分割数必定比后面的多。如果后面的都能分割,又因为每一段都是模式串的前缀,所以一个成功分割的字符串无论你从后面删多少个数它都是合法的分割,这样前面的 dp 值一定能从后面的 dp 值的分割方案中求出。因此前面的 dp 值一定不大于后面的。

于是只要转移最长的 next 就可以了。

其他的注意一下无解判断即可。

时间复杂度 \(O(n)\)。

代码

#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;
}

标签:分割,10000005,题解,后面,符文,KMP,dp
From: https://www.cnblogs.com/zhr0102/p/18620083

相关文章

  • Hexo Next主题本地搜索功能不可用问题解决
    个人博客地址:HexoNext主题本地搜索功能不可用问题解决|一张假钞的真实世界。按照Next主题官网配置步骤(LocalSearch)配置后,站点的“搜索”菜单点击无响应。查看Next主题源代码({Next主题根目录}/hexo-theme-next/layout/_partials/search/index.njk),发现站点优先使用Algolia......
  • GESP202412 八级【树上移动】题解(AC)
    》》》点我查看「视频」详解》》》[GESP202412八级]树上移动题目描述小杨有一棵包含nnn个节点的树,其中节点的编号从1......
  • CF1477D Nezzar and Hidden Permutations 题解
    Description给定一张\(n\)个点\(m\)条边的简单无向图,构造两个排列\(p,q\),使得:对任意\((u,v)\inE\),\((p_u-p_v)(q_u-q_v)>0\).在此基础上,最大化\(\left|\left\{i\|\p_i\neqq_i\right\}\right|\).\(1\leqn,m\leq5\times10^5\)。Solution首先显然如果存在一个......
  • [CERC2014] Parades 题解
    感觉长脑子了。考虑在路线两端点的\(lca\)计算贡献,那么线段可以分两类:\(u\)为\(v\)祖先。\(u,v\)互不为祖先。设\(dp_i\)表示只考虑\(i\)子树内的路线时的答案。引理\(1\):若插入一条以\(i\)为\(lca\)的路径会使以\(i\)的儿子为\(lca\)的路径数量减少,......
  • 题解:AT_arc008_3 [ARC008C] THE☆たこ焼き祭り2012
    思路看到$N\leq1000$,我们立马想到Floyd,把每个人都当作点,把传递小丸子所需的时间当作边权去建边。最后直接跑一遍Floyd就好了。AC代码#include<bits/stdc++.h>usingnamespacestd;usingll=longlong;constintN=1e3+10;intx[N],y[N],t[N],r[N],n;doubled......
  • 题解:CF917A The Monster
    思路分别枚举连续子序列所有起点的可能。用变量来记录左括号和右括号的数量,左括号\(+1\),右括号\(-1\)。对于问号,则通过当前左括号和右括号的数量来判断应该变为右括号还是变为左括号。当右括号数量大于左括号数量时,就可以停止枚举以当前起点为起点的连续子序列了,因为无论怎......
  • 题解:CF603A Alternative Thinking
    思路你猜这个题为什么是A题?很思维的解法。只允许翻转一次,所以最多只会在原答案上加\(2\)。所以我们来讨论仅有的三种可能:加\(2\),要有两段连续的\(0\)或\(1\)。加\(1\),要有一段连续的\(0\)或\(1\)。不加,没有连续的\(0\)或\(1\)。我们的代码模拟上面的三种......
  • 题解:CF626B Cards
    思路枚举三种能够得到该颜色的方法。全是该颜色的卡牌。另外两种卡牌的数量都大于等于一张。另外的两种卡牌,一种大于等于两张,一种为零张,该颜色的卡牌大于等于一张。我们用三个变量来记录每种卡牌出现的次数,然后按照以上的三种方法模拟即可。AC代码#include<bits/stdc++.......
  • 题解:CF1540A Great Graphs
    思路CF思维题。因为我们要让边权值最小,所以可以利用贪心思想先将数组\(d\)进行升序排序。然后再预处理出每一条边的权重。其次我们来想一下如何处理答案,因为这道题说图中不能出现负环和重边,所以我们可以通过加反方向负边的方法来解决这道题。因为对于一条边,这条边之后的所......
  • 题解 - 趣味填空
    题目描述小华的寒假作业上,有这样一个趣味填空题:给出用等号连接的两个整数,如“1234=492”。当然,现在这个等式是不成立的。请你在等号左边整数中的某个位置尝试插入一个乘号,看有没有可能让等式成立。以上面的式子为例,如果写成1234=492,这样就正确了。现在请你编写一个程序......