Atcoder ABC329E Stamp 题解 [ 绿 ] [ 线性 dp ]

Stamp:难点主要在 dp 转移的细节与分讨上,但通过改变状态设计可以大大简化分讨细节的题。

观察

首先要有一个观察:只要某一个前缀能被覆盖出来,那么无论它后面多出来多少,后面的字符串都可以帮他重新覆盖回去。这也就是 dp 为啥没有后效性的原因。

除此之外,还要注意一个字符串不仅能在其他字符串上面,还能被盖在最下层来达到用子串接着覆盖的效果。

暴力 dp 思路

dpi 表示第 i 个字符的前缀能否被覆盖。每次转移的时候枚举当前位和 B 的第几个字符匹配,然后判断某一段是否相等,进行转移即可。这个做法感觉比较难写,但肯定没有假。时间复杂度 O(nm2),显然可过。

更巧妙的 dp 思路

dpi,j 表示 A 匹配到第 i 个,B 匹配到第 j 个是否可行。

那么接下来分为三个情况,这三种情况的前提条件就是 Ai=Bj,否则一定无解:

  • j=1,也就是当前 B 刚开始匹配。也就是说前面的部分只要能被覆盖出就行了,不管他前面匹配到多少个,则 dpi,jdpi,jk=1mdpi1,k
  • 这个字符接着上一个字符覆盖,则 dpi,jdpi,jdpi1,j1
  • 这个字符被盖在最下面,用自己的后缀覆盖。这种情况需要满足的条件是前一个字符串一定要先被覆盖完,才可以在下一个字符串覆盖前覆盖在它的下面。则 dpi,jdpi,jdpi1,m

直接这样转移复杂度也是 O(nm2) 的,但是第一种情况只要记录前一位是否含 1 即可优化为 O(nm)。总体时间复杂度 O(nm)

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
bool dp[200005][10],hv[200005];
int n,m;
char a[200005],b[10];
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m>>a+1>>b+1;
    dp[0][0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(a[i]==b[j])
            {
                dp[i][j]|=dp[i-1][j-1];
                dp[i][j]|=dp[i-1][m];
                if(j==1)dp[i][j]|=hv[i-1];
            }
            hv[i]|=dp[i][j];
        }
    }
    if(dp[n][m])cout<<"Yes";
    else cout<<"No";
    return 0;
}
posted @   KS_Fszha  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示