Censor SCU - 4438 (hash 哈希做法)

Censor SCU - 4438
题意:给你两串字符串w和p。在p中不停删除w,直到p中没有w。输出此时剩余的字符串。

解题思路:

字符串问题考虑hash做法。先求出字符串w的哈希值。然后遍历字符串p,设pos是answer数组中的下标。check 以现在找到的这个点为终点是否可以构成字符串 w。如果可以,pos点向前移动len_w个单位。

难点:

如何以常数级复杂度去check以现在找到的这个点为终点是否可以构成字符串w?
比如我们在123这个数字中找是否存在23。

  1. pos=1 1显然不行,因为pos小于len(23)=2
  2. pos=2 12-0≠23 所以不行
  3. pos=3 123-100=23所以可行
bool check(int pos){
    if (pos<len_w-1) return false;
    if (hash_p[pos]-hash_p[pos-len_w]*Hash[len_w]==hash_w[len_w-1]) return true;
    else return false;
}

AC代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
//#pragma GCC optimize(3,"Ofast","inline")
#define LL long long
#define MT(a,b) memset(a,b,sizeof(a))
const int mod=1000007;
const int maxn=5e6+5;
const int ONF=-0x3f3f3f3f;
const int INF=0x3f3f3f3f;
char w[maxn],p[maxn],ans[maxn];
unsigned LL k=233,Hash[maxn],hash_w[maxn],hash_p[maxn];
int len_w,len_p;
void get(){
    len_w=strlen(w),len_p=strlen(p);
    hash_w[0]=(unsigned LL) w[0];
    Hash[0]=1;
    for (int i=1;i<=len_w;i++){
        hash_w[i]=hash_w[i-1]*k+(unsigned LL) w[i];
        Hash[i]=Hash[i-1]*k;
    }
}

bool check(int pos){
    if (pos<len_w-1) return false;
    if (hash_p[pos]-hash_p[pos-len_w]*Hash[len_w]==hash_w[len_w-1]) return true;
    else return false;
}

void solve(){
    get();
    int pos=0;
    for (int i=0;i<len_p;i++){
        ans[pos++]=p[i];
        hash_p[pos]=hash_p[pos-1]*k+(unsigned LL)p[i];
        if (check(pos)) pos-=len_w;
    }
    for (int i=0;i<pos;i++) printf("%c",ans[i]);
    printf("\n");
}
int main (){
    while (~scanf("%s%s",w,p)){
        solve();
    }
    return 0;
}

你可能感兴趣的:(Censor SCU - 4438 (hash 哈希做法))