最近學了關於字串匹配主要的兩種方法,做了一些題目,發現這可以動態規劃結合,題目中往往有「……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 可...