hdu5745La Vie en rose

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5745

题意:给定一个母串s和一个子串p。问s中有多少个位置可以匹配p,可以不完全匹配,p字符串中的每个位置的字符最多可以变动一次(不变的,与前面的字符交换,与后面的字符交换)。

分析:做多校学姿势。xg的题解解释得很好,但是对于我这种没写过这种优化的人来说还是要看代码学习一遍,我说下我的理解吧。首先xg列出了dp方程:dp[i][j][0]=dp[i-1][j-1][2]&&s[i]==p[j-1]、dp[i][j][1]=(dp[i-1][j-1][0]||dp[i-1][j-1][1])&&s[i]==p[j]、dp[i][j][2]=(dp[i-1][j-1][0]||dp[i-1][j-1][1])&&s[i]==p[j+1]。我们先遍历p的1~m个字符然后用bitset来表示有遍历到当前p[i]时上一层有哪些位置的i是匹配到了前i-1个字符,然后根据当前字符是谁直接去找哪些位置是合法的来更新当前层。这样的话我们就能一层一层的得到1~n的哪些位置是合法的。重点是预处理出26个字符在哪些位置出现过然后用bitset存下来以便能直接&出当前层哪些位置是合法的,还不懂的话看看代码吧。O(n*m/w)。PS:XG增强了数据,减小了时间,变成了3.5s。我的程序过不了了。想过的人自己手写bitset卡卡常数吧。

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#include<math.h>
#include<vector>
#include<string>
#include<stdio.h>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=100010;
const int mod=100000000;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000007;
const int MAX=2000000010;
const ll INF=1ll<<55;
const double pi=acos(-1.0);
typedef double db;
typedef unsigned long long ull;
char s[N],p[5010];
bitset<N>w[30];
bitset<N>dp[2][3];
int main()
{
    int i,j,n,m,t,now,las;
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d", &n, &m);
        scanf("%s%s", s+1, p+1);
        for (i=0;i<26;i++) w[i].reset();
        for (i=1;i<=n;i++) w[s[i]-'a'][i-1]=1;
        now=las=1;dp[now][1].set();
        dp[now][0].reset();dp[now][2].reset();
        for (i=1;i<=m;i++) {
            las=now;now^=1;
            for (j=0;j<3;j++) dp[now][j].reset();
            int pre=p[i-1]-'a',mid=p[i]-'a',suf=p[i+1]-'a';
            if (i>1) dp[now][0]=(dp[las][2]&w[pre])<<1;
            dp[now][1]=((dp[las][0]&w[mid])|(dp[las][1]&w[mid]))<<1;
            if (i<m) dp[now][2]=((dp[las][0]&w[suf])|(dp[las][1]&w[suf]))<<1;
        }
        for (i=m;i<=n;i++)
        if (dp[now][0][i]||dp[now][1][i]) printf("1");
        else printf("0");
        for (i=n+1;i<=n+m-1;i++) printf("0");printf("\n");
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值