SCU-4438-Censor(kmp/哈希+模拟栈)

题目链接:http://acm.scu.edu.cn/soj/problem.action?id=4438
给出s1、s2,要你删除s2中出现的第一个s1,并将剩下的合并成一个新的串,再继续找,直到找不到s1,输出最后的串。
///主要想记录一下模拟栈的使用(比赛的时候都没有想到它T_T,后面想用kmp里面的next数组优化下我的假算法,又不会写T_T)

kmp+模拟栈

KMP算法中,我们在主串中找模拟串,找到后就从起始点的下一位又开始匹配,找下一个模拟串出现的位置。但这里去掉s1后,它的前后有可能合并成一个新的s1串。这里我们可以用一个模拟栈,从前往后处理,在模拟栈中去匹配s1,我们用cou表模拟栈的长度,当出现s1,就执行操作:cou-=strlen(s1),直接O(1)“删掉”出现的s1,然后再往模拟栈中放入后面的字符。需要注意的是cou-=strlen(s1)后,j应该回溯到哪里(需要用一个数组来记录模拟栈每个位置前可以匹配到 s1前缀的长度)

代码:

//pos[cou]表示,模拟栈中,cou为前可以匹配到多少个字符
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
//#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define mid ((l + r)>>1)
#define chl root<<1
#define chr root<<1|1
using namespace std;
typedef unsigned long long ull;
typedef long long LL;
const int manx=5e6+10;
char p[manx],str[manx],s[manx];
int net[manx],pos[manx];
int ls,lp;
void getnext()
{
    net[0]=-1;
    int k=-1,j=0;
    while(j<lp)
    {
        if(k==-1||p[k]==p[j])
            net[++j]=++k;
        else
            k=net[k];
    }
}
int ans_len()
{
    int cou=0,i=0,j=0;
    pos[0]=0;
    while(i<ls)
    {
        str[cou]=s[i++];
        while(j!=-1&&p[j]!=str[cou])
            j=net[j];
        cou++,j++;
        pos[cou]=j;
        if(j==lp)//if这部分就是kmp中改动的部分
        {
            cou-=lp;
            j=pos[cou];
        }
    }
    return cou;
}
int main()
{
    while(scanf("%s%s",p,s)!=EOF)
    {
        lp=strlen(p);
        ls=strlen(s);
        getnext();
        str[ans_len()]='\0';
        printf("%s\n",str);
    }
    return 0;
}
字符串哈希+模拟栈

每次判断模拟栈中倒数Len个字符的哈希值和s1是否相等
///真的每次写哈希都会错,不是哪里没有取模,就是哪里写错了wawadaku

代码:

//hhash[cou]表示前min(cou+1,len)个字符串的哈希值
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
//#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define mid ((l + r)>>1)
#define chl root<<1
#define chr root<<1|1
using namespace std;
//typedef unsigned long long LL;
typedef long long LL;
const LL manx=5e6+10;
const LL mmm=1e7+19;
const LL mod=1e9+7;
char p[manx],s[manx],ans[manx];
LL lp,ls,ttrue,mul[manx],hhash[manx];
int main()
{
    mul[0]=1;
    for(int i=1;i<=5e6;i++)
        mul[i]=(mul[i-1]*mmm)%mod;
    while(scanf("%s%s",p,s)!=EOF)
    {
        ttrue=0;
        lp=strlen(p);
        ls=strlen(s);
        for(int i=0;i<lp;i++)
            ttrue=(ttrue*mmm+p[i])%mod;
        int cou=0,i=0;
        while(i<ls)
        {
            ans[cou]=s[i++];
            if(cou==0)
                hhash[cou]=ans[0];
            else if(cou>=lp)
                hhash[cou]=((hhash[cou-1]-ans[cou-lp]*mul[lp-1]%mod+mod)*mmm%mod+ans[cou])%mod;
            else
                hhash[cou]=(hhash[cou-1]*mmm+ans[cou])%mod;
            if(hhash[cou]==ttrue)
                cou-=lp;
            cou++;
        }
        ans[cou]='\0';
        printf("%s\n",ans);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值