hdu 6170 Two strings 题解(字符串,DP)

本文深入解析了一种针对复杂字符串匹配问题的DP算法,详细介绍了如何处理包含特殊字符的模式串,实现对目标字符串的有效匹配。文章通过实例讲解了状态定义、边界条件设定及状态转移方程的推导过程。

原题链接:
hdu

题意简述

给定两个字符串,第一个只有小写字母,第二个除了小写字母外,还有两个特殊字符 ′ . ′ '.' . ′ ∗ ′ '*' 。其中 ′ . ′ '.' .表示通配符,啥都能配上。 ′ ∗ ′ '*' 珂以让紧前面的单个字符出现任意多次。(而且。。。任意次后这个 ∗ * 珂以保留或不保留,也就是说有继续用的珂能)(比如 a ∗ a* a珂以变成(空), a a a a a aa aa a a a aaa aaa…)。请你匹配两个字符串。

对于一组数据,如果能配上,输出 y e s yes yes,否则输出 n o no no

数据

输入

多组数据。第一行是一个 T ( T &lt; = 15 ) T(T&lt;=15) T(T<=15),表示有 T T T组数据。
接下来 2 T 2T 2T行,每两行表示一组数据,是两行字符串(长度 &lt; = 2500 &lt;=2500 <=2500,平方做法)。

输出

对于每组数据,输出答案。

样例

输入
3
aa
a*
abb
a.*
abb
aab

输出
yes
yes
no

思路

首先说明这个是一个非常毒瘤的字符串 D P DP DP

2500 2500 2500的数据, n 2 n^2 n2是珂以过的。根据这种 D P DP DP的套路,不妨设 d p [ i ] [ j ] dp[i][j] dp[i][j]为:第二个匹配到 i i i,第一个匹配到 j j j,是否能匹配( b o o l bool bool类型)。

(问:为啥是二配一而不是一配二?答:因为这里二特殊,以前的字符串是两个平等,所以让一配二)

然后开始推方程。

首先边界是很明显的: d p [ 0 ] [ 0 ] = t r u e dp[0][0]=true dp[0][0]=true(空配空自然是配上了)

然后我们会发现,如果我们当前位置 i i i是一个 ∗ * ,那么我们就珂以通过神奇的操作把串 2 2 2变成一个空串。所以此时 d p [ i ] [ 0 ] = t r u e dp[i][0]=true dp[i][0]=true
接着我们去枚举和 1 1 1串配到的位置,设为 j j j

如果串 2 2 2 i i i位置和串 1 1 1 j j j位置相等,那么 d p [ i ] [ j ] dp[i][j] dp[i][j]很明显就是 d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i1][j1]了。当然,这个相等还包括通配符。

如果串 2 2 2 i i i位置是 ∗ * ,那么就有点意思了。我们珂以通过让前面的串出现 0 0 0次的膜法让 d p [ i ] [ j ] dp[i][j] dp[i][j]这个状态珂以通过 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j] d p [ i − 2 ] [ j ] dp[i-2][j] dp[i2][j]转移,就看你保不保留 ∗ * 字符了。当然,这个更新关系是或上去的,因为只要一个能匹配我们就能匹配。当然,如果串 1 1 1 j j j位置和 j − 1 j-1 j1位置相等,此时如果 d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i1][j1] d p [ i ] [ j − 1 ] dp[i][j-1] dp[i][j1]成立,我们就珂以通过复制一次使得两串匹配。

至此我们就差不多有方程了。代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
namespace Flandle_Scarlet
{
    #define N 2510
    bool dp[N][N];
    char s1[N],s2[N];int n1,n2;
    void Input()
    {
        scanf("%s%s",s1+1,s2+1);
        n1=strlen(s1+1),n2=strlen(s2+1);
    }

    void Soviet()
    {
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;//边界
        for(int i=1;i<=n2;++i)
        {
            if (s2[i]=='*') dp[i][0]=1;

            for(int j=1;j<=n1;++j)//转移
            {
                if (s2[i]=='.' or s2[i]==s1[j])
                {
                    dp[i][j]=dp[i-1][j-1];
                }
                else if (s2[i]=='*')
                {
                    dp[i][j]=(dp[i-1][j] or dp[i-2][j]);
                    if (s1[j]==s1[j-1] and (dp[i-1][j-1] or dp[i][j-1]))
                    {
                        dp[i][j]=1;
                    }
                }
            }
        }
        puts(dp[n2][n1]?"yes":"no");//输出是否有解
    }

    void IsMyWife()
    {
        if (0)
        {
            freopen("","r",stdin);
            freopen("","w",stdout);
        }
        int t;scanf("%d",&t);
        while(t--)
        {
            Input();
            Soviet();
        }
    }
};
int main()
{
    Flandle_Scarlet::IsMyWife();
    return 0;
}

回到总题解界面

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值