1. 程式人生 > >字符串相關算法合集

字符串相關算法合集

bsp ash ans chang ota stream 維護 ++ 做的

...日後會慢慢補(flag!)先來講講基本的

一.字符串Hash

將字符串用一個數表示,常用的寫法有:

1.自然溢出

2.單Hash

3.雙Hash

前兩個會被精心構造的串卡掉,最後一個雖然目前卡不掉,但是出題人可以卡你常數。

所以這個算法很Naive?不是的

我們來看一道題 bzoj1014

用splay維護字符串的最長公共前綴,那麽問題來了,沒有一種OI比賽中常用的數據結構能維護一個動態的字符串

這時字符串哈希就派上了用場

我們用一個哈希值來表示字符串

在splay上跑一個二分查找就好了

具體的Hash方法網上都有

這裏僅介紹思想

技術分享圖片
#include<iostream>
#include
<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> #include<vector> #include<queue> #include<set> #include<map> #include<stack> #define ll unsigned long long #define pi 3.14 #define eps 1e-9 #define inf 2147483233 #define
m(a) memset(a,0,sizeof(a)) #define M(a) memset(a,127,sizeof(a)) #define REP(i,m,n) for(int i=1;i<=n;i++) #define DWN(i,n,m) for(int i=n;i>=1;i++) #define lowbit(x) x&(-x) #define POJ(n) while(~scanf("%d",&n) && n) using namespace std; int n; inline int read() { int x=0,f=1;
char ch=getchar(); while(!isdigit(ch)){if(ch==-)f=-1;ch=getchar();} while(isdigit(ch)){x=10*x+ch-0;ch=getchar();} return x*f; } inline void write(int x) { int num=0; char buf[15]; while(x)buf[++num]=(x%10)+0,x/=10; while(num)putchar(buf[num--]); putchar(\n); } ll powe[150000]; void makepow() { int i; for(powe[0]=1,i=1;i<140142;i++)powe[i]=powe[i-1]*131; } const int maxn=140142; char str[maxn]; struct SplayTree { int rt,Size; int son[maxn][2],f[maxn],size[maxn]; ll val[maxn],hsh[maxn]; char s[maxn]; inline void pushup(int x){size[x]=size[son[x][0]]+size[son[x][1]]+1;hsh[x]=hsh[son[x][0]]+powe[size[son[x][0]]]*s[x]+powe[size[son[x][0]]+1]*hsh[son[x][1]];} inline void Rotate(int x,int type) { int y=f[x]; son[y][!type]=son[x][type]; f[son[x][type]]=y; f[x]=f[y]; if(f[x])son[f[y]][son[f[y]][1]==y]=x; son[x][type]=y; f[y]=x; size[x]=size[y],hsh[x]=hsh[y]; pushup(y); } inline void splay(int x,int goal) { while(f[x]!=goal) { if(f[f[x]]==goal) { if(son[f[x]][0]==x)Rotate(x,1); else Rotate(x,0); } else { int y=f[x],z=f[y]; if(son[z][0]==y) { if(son[y][0]==x)Rotate(y,1),Rotate(x,1); else Rotate(x,0),Rotate(x,1); } else { if(son[y][1]==x)Rotate(y,0),Rotate(x,0); else Rotate(x,1),Rotate(x,0); } } } if(goal==0) rt=x; } inline int rank(int x,int k) { if(k<=size[son[x][0]]) return rank(son[x][0],k); else if(k==size[son[x][0]]+1) return x; else return rank(son[x][1],k-size[son[x][0]]-1); } inline int build(int l,int r,int id) { if(l>r)return 0; int x=++Size,mid=(l+r)>>1; f[x]=id; s[x]=str[mid]; son[x][0]=build(l,mid-1,x); son[x][1]=build(mid+1,r,x); pushup(x); return x; } inline void insert(int k,char val) { int x=rank(rt,k),y=rank(rt,k+1); splay(x,0),splay(y,x); s[++Size]=val; f[Size]=y,son[y][0]=Size; pushup(Size); pushup(y); pushup(x); } inline void change(int k,int val) { int x=rank(rt,k); splay(x,0); s[x]=val; pushup(x); } inline int bisearch(int kx,int ky) { int l=0,r=n,mid; while(l<=r) { mid=l+r>>1; if(ky+mid>n+2) { r=mid-1; continue; } int x=rank(rt,kx-1),y=rank(rt,kx+mid); splay(x,0),splay(y,x); ll temp=hsh[son[y][0]]; x=rank(rt,ky-1),y=rank(rt,ky+mid); splay(x,0),splay(y,x); if(temp==hsh[son[y][0]])l=mid+1; else r=mid-1; } return r; } }splay; int main() { int m,i,j,k,x,y; char op[10],val[10]; scanf("%s%d",str+1,&m); makepow(); n=strlen(str+1); splay.rt=splay.build(0,n+1,0); for(i=1;i<=m;i++) { scanf("%s",op); if(op[0]==I) { scanf("%d%s",&x,val); splay.insert(x+1,val[0]); n++; } else if(op[0]==R) { scanf("%d%s",&x,val); splay.change(x+1,val[0]); } else { scanf("%d%d",&x,&y); if(x>y) swap(x,y); if(x!=y) printf("%d\n",splay.bisearch(x+1,y+1)); else printf("%d\n",n-x+1); } } return 0; }
bzoj1014 技術分享圖片
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <int, int> pii;
const int N = 2e6 + 5;
const int seed = 131;
int mod[2] = {1000000007, 1000000009};
char S[N]; //輸入字符串
int F[2][N]; //seed ^ a
int pre[2][N];
int n, g, w;
int tim, vis[N], ans[N];
map <pii, int> cnt;

void pre_init(){ //deal with seed ^ n
    for(int t = 0; t <= 1; t++){
        F[t][0] = 1;
        for(int i = 1; i < N; i++){
            F[t][i] = (LL)F[t][i-1] * seed % mod[t];
        }
    }
}

pii Hash(string s){ //Hash a string
    int ret[2] = {0};
    int len = s.size();
    for(int t = 0; t <= 1; t++){
        for(int i = 0; i < len; i++){
            ret[t] = ((LL) ret[t] * seed + s[i]) % mod[t];
        }
    }
    return pii(ret[0], ret[1]);
}

pii Hash(int l, int r)
{
    int ret[2];
    for(int t = 0; t <= 1; t++){
        ret[t] = (pre[t][r] - (LL)pre[t][l-1] * F[t][r-l+1] % mod[t] + mod[t]) % mod[t];
    }
    return pii(ret[0], ret[1]);
}

void pre_solve(){
    int len = strlen(S + 1);
    for(int t = 0; t <= 1; t++){
        pre[t][0] = 0;
        for(int i = 1; i <= len; i++){
            pre[t][i] = ((LL)pre[t][i-1] * seed + S[i]) % mod[t];
        }
        for(int j = len + 1, i = 1; i <= w - 1; i++, j++){
            pre[t][j] = ((LL)pre[t][j-1] * seed + S[i]) % mod[t];
        }
    }
}

bool check(int id){
    tim++;
    int l = id, r = id + w - 1;
    for(int i = 1; i <= n; i++, l += w, r += w){
        pii t = Hash(l, r);
        if(!cnt.count(t)) return 0; //目標串中沒有這個子串
        int p = cnt[t];
        if(vis[p] == tim) return 0; //目標串出現了大於1次
        vis[p] = tim; ans[i] = p;
    }
    printf("YES\n");
    for(int i = 1; i <= n; i++) cout << ans[i] <<" "; cout << endl;
    return 1;
}

int main()
{
    pre_init();
    scanf("%d%d%s", &n, &w, S+1);
    pre_solve();
    scanf("%d", &g);
    for(int i = 1; i <= g; i++){
        scanf("%s", S);
        pii t = Hash(S);
        cnt[t] = i;
    }
    bool mark = 0;
    for(int i = 1; i <= w; i++){
        if(check(i)){
            mark = 1;
            break;
        }
    }
    if(mark == 0){
        printf("NO\n");
    }
    return 0;
}
雙哈希代碼(源網)

模數之一要用19260817這個大質數

有奇效

二.K(kan)M(mao)P(pian)

一類用於單字符串匹配的O(n+m)的算法

解釋也不是很好講

直接上模板吧

技術分享圖片
#include<cstdio>
#include<string>
#include<iostream>
using namespace std;
int p[101];
int main()
{
    string a,b;
    cin>>a>>b;
    int n=a.length(),m=b.length();
    a=" "+a;b=" "+b;
    int j=0;
    for(int i=2;i<=m;i++)
    {
            while(j>0&&b[j+1]!=b[i])j=p[j];
            if(b[j+1]==b[i])j++;
            p[i]=j;
            }
    j=0;
    for(int i=1;i<=n;i++)
    {
            while(j>0&&b[j+1]!=a[i])j=p[j];
            if(b[j+1]==a[i])j++;
            if(j==m){printf("%d",i-m+1);break;}
            }
    return 0;
}
View Code

三.AC自動機

/*多麽希望AC和自動能夠換一下*/

就是在Trie樹上跑KMP

每個點都有一個Fail指針表示如果這個點匹配不上跳到哪個地方

四.字符串dp

1.O(n^2)可以過的dp

設計狀態一般至少為dp[i][j]表示第一個串的前i個和第二個串的前j個怎麽這麽樣

2.AC自動機上的dp

兩種做法:第一個是構建Fail樹,跑樹形dp

另外一種不能在Fail樹上做的,一般有一維表示"當前在AC自動機的x號節點上"

字符串相關算法合集