日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

算法五——字符串匹配(中)

發(fā)布時(shí)間:2023/12/10 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 算法五——字符串匹配(中) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章出處:極客時(shí)間《數(shù)據(jù)結(jié)構(gòu)和算法之美》-作者:王爭(zhēng)。該系列文章是本人的學(xué)習(xí)筆記。

1文本編輯器中的查找功能怎么實(shí)現(xiàn)

在word中有一個(gè)功能:查找某個(gè)字符串,將其替換為另一個(gè)字符串,就會(huì)用到這個(gè)功能。
需要新算法的原因是:word可能特別長(zhǎng),BF算法可能性能退化很嚴(yán)重;RK算法需要設(shè)計(jì)一個(gè)能夠處理所有字符串的哈希函數(shù),這并不容易。
  新的算法BM,根據(jù)實(shí)驗(yàn)統(tǒng)計(jì),其性能是KMP的3~4倍。

2算法的核心思想

我們把模式串和主串匹配的過(guò)程看做是模式串在主串中不斷向后匹配的過(guò)程。當(dāng)遇到不匹配的字符的時(shí)候BF和RK都是向后移動(dòng)一位。那可不可以多移動(dòng)幾位呢?
  
  在例子中主串中的c在模式串中不存在,可以直接將模式串移動(dòng)到c后面。
  
  由現(xiàn)象找規(guī)律。當(dāng)遇到不匹配的字符的時(shí)候,有什么規(guī)律可以讓模式串一次移動(dòng)好幾位呢?
  BM算法就是在找這種規(guī)律。規(guī)律的核心分為壞字符和好后綴兩個(gè)規(guī)則。

2.1 壞字符規(guī)則

我們從模式串的末尾往前倒著匹配,當(dāng)我們發(fā)現(xiàn)某個(gè)字符沒(méi)法匹配的時(shí)候,把這個(gè)沒(méi)有匹配的字符叫做壞字符(主串中的字符串)。
  當(dāng)發(fā)生不匹配的時(shí)候,我們把壞字符在模式串中出現(xiàn)的位置記為si,如果壞字符在模式串中存在,則找到在模式串中最右邊的位置記為xi,不存在xi=-1。模式串向后移動(dòng)的位數(shù):si-xi。
  
  我們用數(shù)組bc[i]表示字符i在模式串中出現(xiàn)的位置。當(dāng)模式串中有多個(gè)字符i的時(shí)候,bc[i]保存的是i的最大位置。這是為了防止過(guò)度偏移。
  在上圖中,當(dāng)再次發(fā)生不匹配的時(shí)候bc[a]=0。這時(shí)候偏移2-0=2。
  壞字符規(guī)則代碼部分。

// 數(shù)據(jù)包含的字符范圍private static final int SIZE = 256;private void generateBC(char[] b, int m, int[] bc) {Arrays.fill(bc, -1);for (int i = 0; i < m; i++) {bc[(int) b[i]] = i;}}/*** 使用BM算法,查找字符串b在字符串a(chǎn)中出現(xiàn)的首位置。如果沒(méi)有出現(xiàn),返回-1.* * @param a* @param b* @return*/public int bm(char[] a, char[] b) {int n = a.length, m = b.length;int[] bc = new int[SIZE];generateBC(b, m, bc);int i = 0;while (i <= n - m) {int j = m - 1;while (j >= 0 && a[i + j] == b[j]) {j--;}if (j == -1)return i;int x = j - bc[(int) a[i + j]]; i = i + x;}return -1;}

特殊情況下比如主串是 aaaaaaaaaaaaaaaa,模式串是 baaaa。模式串不但不會(huì)向后移動(dòng),還會(huì)向前走。這是因?yàn)?#xff0c;假設(shè)i=0,j從m-1開(kāi)始匹配,匹配到j(luò)=0的時(shí)候發(fā)生不匹配,壞字符為a,suffix[a]=4,j-suffix[‘a(chǎn)’]=-4,i=i-4。i的值 不斷減小。
  這個(gè)時(shí)候就需要第二個(gè)規(guī)則了。

2.2 好后綴規(guī)則

模式串和主串已經(jīng)匹配好的部分,叫做好后綴。記為{u}。
  模式串中可能只有一個(gè)好后綴,也有可能包含至少2個(gè)好后綴。分兩種情況討論。

2.2.1 模式串中至少包含2個(gè)u


  我們查找{u},在模式串中是否還有出現(xiàn)。從右向左查找第一個(gè)出現(xiàn){u}的匹配串記為{u*}。將{u*}滑動(dòng)到主串與{u}匹配的位置。也就是不同于好后綴,但是是最后一次出現(xiàn)u的起始位置。

2.2.2 模式串中只有一個(gè)u

如果不能在模式串中找到另外一個(gè)u,怎么辦?直接滑動(dòng)到主串{u}的后面,會(huì)有過(guò)渡滑動(dòng)的可能性。
  介紹幾個(gè)概念。
  字符串s的后綴子串是指 與最后一個(gè)字符跟 字符串s 對(duì)齊的子串,比如 abc 的后綴子串包括 c, bc。前綴子串是指起始字符與字符串s對(duì)齊的子串。abc的前綴子串包括a,ab。
  如果好后綴在模式串中不存在可匹配的子串,那在我們一步一步往后滑動(dòng)模式串的過(guò)程中,只要主串中的{u}與模式串有重合,那肯定就無(wú)法完全匹配。但是當(dāng)模式串滑動(dòng)到前綴與主串中{u}的后綴有部分重合的時(shí)候,就有可能會(huì)存在完全匹配的情況。
  所以,針對(duì)這種情況,需要考察好后綴的后綴子串,是否存在跟模式串的前綴子串匹配的。
  
 例如圖中第一次發(fā)生不匹配的時(shí)候,模式串位置j=4,對(duì)應(yīng)主串位置x=7,好后綴是字符串bc。bc在模式串中只出現(xiàn)了一次。如果我們整體向右移動(dòng)模式串長(zhǎng)度 m(=7),那就可能錯(cuò)過(guò)一次匹配。因?yàn)樵谶@里好后綴bc,有匹配的前綴子串c。將前綴字符串c和好后綴子串c對(duì)應(yīng)起來(lái),再次匹配即可。

2.2.3 好后綴規(guī)則編碼

首先我們總結(jié)好后綴規(guī)則。令{u}=好后綴。
如果模式串中包含另外一個(gè)子字符串{u*}={u},則找到這個(gè)這個(gè){u*}的起始位置,移動(dòng)。

如果模式串中不包含另外一個(gè)子字符串{u*},則找到{u}的最長(zhǎng)前綴匹配字符串{v}的起始位置,移動(dòng)。

先考慮怎么表示一個(gè)字符串的后綴。對(duì)于所有后綴字符串的結(jié)束字符都是一樣的。我們可以用不同的長(zhǎng)度表示后綴字符串 。用數(shù)組suffix[i]表示長(zhǎng)度為i的后綴字符串 ,在模式串中出現(xiàn)的起始位置。

用prefix[i]表示長(zhǎng)度為i的后綴字符串是否有匹配的前綴子串。

 
 怎么得到這兩個(gè)數(shù)組有編碼上的技巧。具體看代碼。

2.3 怎么選擇壞字符和好后綴規(guī)則

我們可以分別計(jì)算好后綴和壞字符往后滑動(dòng)的位數(shù),然后取兩個(gè)數(shù)中最大的,作為模式串往后滑動(dòng)的位數(shù)。

3 BM代碼實(shí)現(xiàn)

public class BM {// 數(shù)據(jù)包含的字符范圍private static final int SIZE = 256;private void generateBC(char[] b, int m, int[] bc) {Arrays.fill(bc, -1);for (int i = 0; i < m; i++) {bc[(int) b[i]] = i;}}/*** 使用BM算法,查找字符串b在字符串a(chǎn)中出現(xiàn)的首位置。如果沒(méi)有出現(xiàn),返回-1.* * @param a* @param b* @return*/public int bm(char[] a, char[] b) {int n = a.length, m = b.length;int[] bc = new int[SIZE];int[] suffix = new int[m];boolean[] prefix = new boolean[m];generateBC(b, m, bc);generatorGS(b, m, suffix, prefix);int i = 0;while (i <= n - m) {int j = m - 1;while (j >= 0 && a[i + j] == b[j]) {j--;}if (j == -1)return i;int x = j - bc[(int) a[i + j]];int y = 0;if(j < m - 1) {y = moveByGS(b, m, j, suffix, prefix);} i = i + Math.max(x, y);}return -1;}/*** 生成后綴子串完整匹配的位置 后綴子串可匹配的最長(zhǎng)前綴子串* * @param b* @param m* @param suffix* @param prefix*/private void generatorGS(char[] b, int m, int[] suffix, boolean[] prefix) {Arrays.fill(suffix, -1);for (int i = 0; i < m - 1; i++) {int k = 0;int j = i;while (j >= 0 && b[j] == b[m - 1 - k]) {j--;k++;suffix[k] = j + 1;}if (j == -1) {prefix[k] = true;}}}/*** 按照好后綴規(guī)則,當(dāng)j位置發(fā)生比匹配的時(shí)候應(yīng)該移動(dòng)幾步* * @param b* @param j* @param suffix* @param prefix* @return*/private int moveByGS(char[] b, int m, int j, int[] suffix, boolean[] prefix) {int k = m - 1 - j;//好后綴長(zhǎng)度if(suffix[k] != -1) return j - suffix[k] + 1;for(int r = j+2; r < m; r++) {if(prefix[m - r] == true) {return r;}}return 0;}public static void main(String[] args) {String a = "abcacabdc";String b = "abd";int postion = new BM().bm(a.toCharArray(), b.toCharArray());System.out.println(postion);}}

總結(jié)

以上是生活随笔為你收集整理的算法五——字符串匹配(中)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。