1. 程式人生 > >字串演算法(KMP+MANACHER+EX_KMP)總結

字串演算法(KMP+MANACHER+EX_KMP)總結

字串演算法:
1、 KMP演算法
2、 MANACHER演算法
3、 EX_KMP演算法

KMP演算法程式碼:

求Next陣列原始碼:

next[0]=next[1]=0; k=0;
for (i=2;i<=m;i++)
{ while (k>0&&T[i]!=T[k+1]) k=next[k];
 if (T[i]==T[k+1]) k++;
 next[i]=k;
}

KMP演算法匹配原始碼:

k=0; //k表示S串當前位匹配到T串第k位
for (i=1;i<=n;i++)
{
 while (k>0&&S[i]!=T[k+1
]) k=next[k]; //跳到可匹配處 if (S[i]==T[k+1]) k++; if (k==m) { k=next[k]; ans++; } } //ans表示出現次數

MANACHER演算法程式碼:

ans[0]=ans[1]=0; p=1;
for (i=2;i<=n;i++) //列舉迴文中點
{ ans[i]=max(0, min(ans[2*p-i],p+ans[p]-i) );  //不超出最右端就直接取
while (S[i-ans[i]]==S[i+ans[i]]) ans[i]++; //暴力擴充套件
if (i+ans[i]>p+ans[p]) p=i; } //更新最右斷點

EX_KMP演算法程式碼:(與MANACHER相似)

計算Next陣列原始碼:

next[0]=m;
for (i=0;T[i]==T[i+1];i++);
next[1]=i; p=1;
for (i=2;i<m;i++) {
  u=p+next[p]; //u為最右端位置
  if (i+next[i-p]<u) next[i]=next[i-p];//如果答案可以直接取
  else { //如果答案需要探索
    for (j=max(u-i,0);T[i+j]==T[j];j++);
    next[i]=j; p=i;
  }
}

與S串匹配原始碼:

for
(i=0;i<m&&S[i]==T[i];i++); ex[0]=i; p=0; for (i=1;i<n;i++) { u=p+ex[p]; //u為最右端位置 if (i+next[i-p]<u) ex[i]=next[i-p]; //如果答案可以直接取得 else { //如果答案需要探索 for (j=max(u-i,0);j<=m&&S[i+j]==T[j];j++); ex[i]=j; p=i; } }

字串讀入:

一般情況使用KMP、MANACHER演算法前,字串從STRING[1]開始記錄,EX_KMP從STRING[0]開始記錄。

KMP的應用:

1、 求字串T在S中出現的次數

poj3461 Oulipo

做法:直接用KMP進行匹配。

2、求字串中的迴圈節
例子

poj2185 Milking Grid

做法:行方向和列方向分別找出最小迴圈節長R和C,面積S就是R*C。

HDU3746 Cyclic Nacklace

做法:
若有完整的迴圈節如圖中例1,則無需新增。

否則就有殘缺的迴圈節如圖中例2,則求出補齊所需的花費。
具體程式碼片段如下:

int cir=Len-next[Len]; //迴圈節長
if (Len%cir==0&&cir!=Len) //是否完整
        printf("0\n");
else    printf("%d\n",cir-Len%cir);

MANACHER的應用

求迴文長度

兩種情況:
1、如字串ababac 最長迴文就是以S[3]為中點,長度為5的迴文串。
2、如字串babbac 最長迴文就是以S[3]和S[4]兩個字元為中點,長度為4的迴文串。

情況2可以通過預處理轉換為情況1。方法:每兩個字元間插入一個從未在原串出現過的相同字元,如’#’、’|’、’*’…同時為了防止陣列越界,頭尾分別加入兩個不同的奇怪字元。
用此方法處理的字串babbac為 @#b#a#b#b#a#c#!

Len=Len*2+3
迴文長度為ans[i]-1 (不包括新增的字元,可證明)

HDU3068 最長迴文

做法:直接做MANACHER。

HDU3613 Best Reward

做法:做MANACHER同時判斷,若迴文串包括左端點,則記錄pl[ans[i]-1]=true 表示前ans[i]-1個字元組成迴文串。 若包括右端點,則記錄pr[ans[i]-1]=true 表示後ans[i]-1個字元組成迴文串。

具體程式碼片段如下:

if (i-ans[i]==1) pl[ans[i]-1]=1;
if (i+ans[i]==Len) pr[ans[i]-1]=1;

EX_KMP的應用
給出一個長度n的字串S[0..n-1]
和一個長度m的字串T[0..n-1]
問S的哪個字尾和T具有最長的公共字首

HDU4333 Revolving Digits
做法:可以把數字再複製一遍,用EX_KMP匹配,求出每一個字尾與原數相同的數字個數Next[i],如果大於原長則相同,否則比較下一個不同的數字。

注意:要求比較的是不同的數字,如果有迴圈節,則需要除去迴圈節個數(KMP)。

附三個完整模板:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int kmp()
{
    char t[100],s[100]; scanf("%s%s",t+1,s+1);
    int next[100],ans=0,k;

    int len1=strlen(t+1),len2=strlen(s+1);

    next[0]=next[1]=k=0;

    for (int i=2;i<=len1;i++)
    {
        while (k>0&&t[k+1]!=t[i]) k=next[k];
        if (t[k+1]==t[i]) k++;
        next[i]=k;
    }

    k=0;
    for (int i=1;i<=len2;i++)
    {
        while (k>0&&t[k+1]!=s[i]) k=next[k];
        if (t[k+1]==s[i]) k++;
        if (k==len1) {
            ans++; k=next[k];
        }
    }
    printf("%d",ans);
    return 0;
}

int manacher()
{
    char t[100],s[100]; scanf("%s",t+1);
    int len=2; s[1]='@'; s[2]='#';
    for (int i=1;i<strlen(t);i++)
    {
        s[++len]=t[i]; s[++len]='#';
    } s[++len]='!';

    int ans[100],p=1; memset(ans,0,sizeof(ans));

    for (int i=2;i<=len;i++)
    {
        ans[i]=max(0,min(ans[2*p-i],p+ans[p]-i));
        while (s[i+ans[i]]==s[i-ans[i]]) ans[i]++;
        if (ans[i]+i>ans[p]+p) p=i;
    }
    for (int i=2;i<=len-1;i++)  printf("%c%d ",s[i],ans[i]-1);

    return 0;
}

int Ex_kmp()
{
    char t[100],s[100]; scanf("%s%s",t,s);
    int next[100],ex[100];
    int len1=strlen(t),len2=strlen(s);

    next[0]=len1;
    for (next[1]=0;t[next[1]]==t[next[1]+1];next[1]++);

    int p=1;
    for (int i=2;i<len1;i++)
    {
        int u=p+next[p];
        if (i+next[i-p]<u) next[i]=next[i-p];
        else {
            int j;
            for (j=max(u-i,0);t[i+j]==t[j];j++);
            next[i]=j; p=i;
        }
    }

    int i;
    for (i=0;i<=len1&&s[i]==t[i];i++);
    ex[0]=i; p=0;
    for (i=1;i<len2;i++)
    {
        int u=p+ex[p];
        if (i+next[i-p]<u) ex[i]=next[i-p];
        else {
            int j;
            for (j=max(u-i,0);t[j]==s[i+j];j++);
            ex[i]=j; p=i;
        }
    }

    for (int i=0;i<len2;i++) printf("%d ",ex[i]);

    return 0;
}

int main()
{
    //kmp();
    //manacher();
    //Ex_kmp();
    return 0;
}