動態規劃和字串匹配(KMP AC自動機)

2021-06-13 18:27:57 字數 3021 閱讀 5162

最近學了關於字串匹配主要的兩種方法,做了一些題目,發現這可以動態規劃結合,題目中往往有「……t是s的子串……」這類字眼,而且答案要求「最少、最多、多少種」一類的問題。

解決這類問題往往是要記錄乙個「匹配到哪一位」的狀態,然後考慮當前狀態可以更新哪些新的狀態,下面列舉兩道題目:

kmp

有兩個字串s和t(1 ≤ |s| ≤10000, 1 ≤ |t| ≤ 1000),問題是至少要從字串s中刪掉多少個字元,才能使得字串t不是字串s的子串。

很容易想到乙個這樣的動態規劃:以f(i,j)記錄s字串第i位,匹配了t的前j位,最少需要刪除多少位。然後對於每一位,有刪除和不刪除兩種情況。下面是乙個例子:

i        

i + 1sa

bata

bcj

我們用當前的狀態去更新新的狀態,現在需要考慮第i+1位的a是否刪除,如果刪除,匹配的個數j不變,那麼:f(i+1,j)= f(i,j)+ 1;如果不刪除,匹配的個數是多少呢?

i        

i + 1sa

bata

bct』a

bcj

當然,不能夠乙個個列舉。這時可以發現,求新的匹配的個數正好是kmp中匹配失敗後的所需要求的。

那麼,這樣時間複雜度貌似就是預處理的o(|t|)加上狀態o(|s| × |t|)。但後來發現求新的匹配個數時超時,因為很多求next的過程是一樣的。如:

1      

2       

3       

i = 4

5      

6      sa

babc

atab

abat』

abab

at』』ab

a 首先,i = 4, j = 4,也就是t串,現在匹配了4個,現在考慮不刪除i+1位,則要求出匹配的個數,得到了t』,然而t』就是f(i,next[j]),發現還是不行,則又有t』』 : f(i,next[ next[j ] ]),還是不行,那麼就是乙個也匹配不了。那麼在求完了f(i,j)後,結果也是t』、t』』的結果,所以可以同時計算這幾個的結果。這樣就不超了。

ac自動機

給出n(1 ≤ n ≤ 20, 長度小於 20)個字串,要求構造長度為k的字串,使得這n個字串出現的次數最多。

樣例輸入:

3 7

abacb

abacb

樣例輸出:

樣例解釋:

構造出  abacbcb,則有1個aba,2個cb,1個abacb。

設計乙個這樣的動態規劃:f(i, j)記錄下,已經了構造第i個字元,匹配的狀態為字母樹的標號為j的結點(這個可以用靜態陣列來儲存字母樹,然後陣列第j個結點就是標號為j的結點),最多可以出現多少個字串,然後可以列舉第 i+1 構造的字母,去更新f(i+1, ?)。這個可以在字母樹上沿著失敗指標走一次即可。

#include #include using namespace std;

const int inf = 0x3f3f3f3f;

char s[10007], t[1007];

int next[1007];

int f[10007][1007];

int main()

for (int i = 0; i < n; i ++)

for (int j = 0; j < m; j ++)

f[i][j] = inf; //全部為無窮大

if (s[0] == t[0])

else

f[0][0] = 0;

for (int i = 0; i < n - 1; i ++)

tmp2 <?= f[i][tmp + 1];

f[i][tmp + 1] = inf;

if (t[tmp + 1] == s[i + 1]) tmp ++;

f[i + 1][tmp + 1] <?= tmp2;}}

int ans = inf;

for (int j = 0; j < m; j ++) //結果不能匹配m個,

ans <?= f[n - 1][j];

printf("%d\n", ans);

return 0;

}

#include #include using namespace std;

struct trie

}mem[307], *root = mem, *que[307]; //靜態陣列, 下標為0的是根

int tot, f[1007][307];

char dat[17];

trie* new()

int main()

now -> cnt ++;

}int head = 0, tail = 0;

que[0] = root;

root -> next = null; //建立ac自動機

while (head <= tail)

tmp = tmp -> next;}}

}}

memset(f, -1, sizeof(f)); //初始化

f[0][0] = 0;

for (int i = 0; i < k; i ++) //構造第i位

for (int j = 0; j <= tot; j ++) //點的編號

if (f[i][j] > -1)

now = now -> son[p];

trie *tmp = now;

int cntf = 0; //計數:可以新增多少個

while (tmp)

f[i+1][now-mem] >?= f[i][j] + cntf;}}

int ans = 0;

for (int i = 0; i <= tot; i ++) ans >?= f[k][i];

printf("%d\n", ans);

return 0;

}

字串匹配問題 遞迴和動態規劃

題目 給定字串str,其中絕對不含有字元 和 再給定字串exp,其中可以含有 或 字元不能是exp的首字元,並且任意兩個 字元不相鄰。exp中的 代表任何乙個字元,exp中的 表示 的前乙個字元可以有0個或者多個。請寫乙個函式,判斷str是否能被 exp匹配。舉例 str abc exp abc 返...

通用字串匹配,動態規劃

題目如下 給定乙個字串 s 和乙個字元模式 p 實現支援 和 的正規表示式匹配。匹配任意單個字元。匹配零個或多個前面的元素。匹配應該覆蓋整個字串 s 而不是部分字串。說明 示例 1 輸入 s aa p a 輸出 false 解釋 a 無法匹配 aa 整個字串。示例 2 輸入 s aa p a 輸出 ...

帶萬用字元的字串匹配 動態規劃

描述 萬用字元是一類鍵盤字元,當我們不知道真正字元或者不想鍵入完整名字時,常常使用萬用字元代替乙個或多個真正字元。萬用字元有問號 和星號 等,其中,可以代替乙個字元,而 可以代替零個或多個字元。你的任務是,給出乙個帶有萬用字元的字串和乙個不帶萬用字元的字串,判斷他們是否能夠匹配。例如,1?456 可...