zoukankan      html  css  js  c++  java
  • 字符串搜索算法

    1.单模式匹配

    就是在一些文本中查找某一个子字符串的算法,效率较高的有以下几种。

    KMP算法:全称Knuth-Morris-Pratt算法 预处理时间Θ(m) 匹配搜索时间 Θ(n)

    代码
    1 #include <stdio.h>
    2 #include <stdlib.h>
    3 #include <string.h>
    4
    5  const int * get_prefix(const char * P)
    6 {
    7 int * pi = (int *)malloc(sizeof(int) * strlen(P));
    8 pi[0] = -1;
    9 int i = 1;
    10 int j = -1;
    11 while (P[i])
    12 {
    13 while (j >= 0 && P[j + 1] != P[i])
    14 {
    15 j = pi[j];
    16 }
    17 if (P[j + 1] == P[i])
    18 {
    19 ++j;
    20 }
    21 pi[i] = j;
    22 ++i;
    23 }
    24 return pi;
    25 }
    26
    27  void kmp_match(const char * T, const char * P)
    28 {
    29 const int * pi = get_prefix(P);
    30 int i = 0;
    31 int j = -1;
    32 while (T[i])
    33 {
    34 while (j >= 0 && P[j + 1] != T[i])
    35 {
    36 j = pi[j];
    37 }
    38 if (P[j + 1] == T[i])
    39 {
    40 ++j;
    41 }
    42 if (0 == P[j + 1])
    43 {
    44 printf("%s\n", T + i - j);
    45 j = pi[j];
    46 }
    47 ++i;
    48 }
    49 free(pi);
    50 }
    51
    52  int main(int argc, char * argv[])
    53 {
    54 kmp_match("abcdabcdabcdabcd", "abc");
    55
    56 return 0;
    57 }
    58
    59
    60 参考:《算法导论》
    61
    62  ---------------------------------------------------------------------------
    63
    64  /*
    65 * Knuth-Morris-Pratt 字符串匹配算法的三种实现。
    66 * 匹配部分都一样,差异只在求 next 数组。:)
    67 */
    68
    69 #include <stdio.h>
    70 #include <stdlib.h>
    71 #include <string.h>
    72
    73  /*
    74 * 实现一
    75 */
    76  char * kmp1(char * content, char * pattern)
    77 {
    78 int i;
    79 int j;
    80 int len;
    81 int * next;
    82
    83 if (NULL == content || NULL == pattern)
    84 {
    85 return NULL;
    86 }
    87
    88 len = strlen(pattern);
    89 next = (int *)malloc(len * sizeof(int));
    90
    91 /* Get the "next" array. */
    92 next[0] = -1;
    93 for (i = 1; pattern[i] != 0; ++i)
    94 {
    95 j = next[i - 1];
    96 while (pattern[i - 1] != pattern[j] && j >= 0)
    97 {
    98 j = next[j];
    99 }
    100 next[i] = j + 1;
    101 }
    102
    103 /* Match. */
    104 i = 0;
    105 j = 0;
    106 while (content[i] && pattern[j])
    107 {
    108 if (content[i] == pattern[j])
    109 {
    110 ++i;
    111 ++j;
    112 }
    113 else
    114 {
    115 j = next[j];
    116 if (-1 == j)
    117 {
    118 ++i;
    119 ++j;
    120 }
    121 }
    122 }
    123
    124 free(next);
    125
    126 if (pattern[j])
    127 {
    128 return NULL;
    129 }
    130 else
    131 {
    132 return &content[i - j];
    133 }
    134 }
    135
    136  /*
    137 * 实现二
    138 */
    139  char * kmp2(char * content, char * pattern)
    140 {
    141 int i;
    142 int j;
    143 int len;
    144 int * next;
    145
    146 if (NULL == content || NULL == pattern)
    147 {
    148 return NULL;
    149 }
    150
    151 len = strlen(pattern);
    152 next = (int *)malloc(len * sizeof(int));
    153
    154 /* Get the "next" array. */
    155 next[0] = -1;
    156 i = 0;
    157 j = -1;
    158 while (pattern[i])
    159 {
    160 if (-1 == j || pattern[i] == pattern[j])
    161 {
    162 ++i;
    163 ++j;
    164 next[i] = j;
    165 }
    166 else
    167 {
    168 j = next[j];
    169 }
    170 }
    171
    172 /* Match. */
    173 i = 0;
    174 j = 0;
    175 while (content[i] && pattern[j])
    176 {
    177 if (content[i] == pattern[j])
    178 {
    179 ++i;
    180 ++j;
    181 }
    182 else
    183 {
    184 j = next[j];
    185 if (-1 == j)
    186 {
    187 ++i;
    188 ++j;
    189 }
    190 }
    191 }
    192
    193 free(next);
    194
    195 if (pattern[j])
    196 {
    197 return NULL;
    198 }
    199 else
    200 {
    201 return &content[i - j];
    202 }
    203 }
    204
    205 /*
    206 * 实现三
    207 *
    208 * 实现二的改进,改进处见注释。
    209 */
    210 char * kmp3(char * content, char * pattern)
    211 {
    212 int i;
    213 int j;
    214 int len;
    215 int * next;
    216
    217 if (NULL == content || NULL == pattern)
    218 {
    219 return NULL;
    220 }
    221
    222 len = strlen(pattern);
    223 next = (int *)malloc(len * sizeof(int));
    224
    225 /* Get the "next" array. */
    226 next[0] = -1;
    227 i = 0;
    228 j = -1;
    229 while (pattern[i])
    230 {
    231 if (-1 == j || pattern[i] == pattern[j])
    232 {
    233 ++i;
    234 ++j;
    235
    236 /* 此处是对实现二的改进。 */
    237 if (pattern[i] == pattern[j])
    238 {
    239 next[i] = next[j];
    240 }
    241 else
    242 {
    243 next[i] = j;
    244 }
    245 }
    246 else
    247 {
    248 j = next[j];
    249 }
    250 }
    251
    252 /* Match. */
    253 i = 0;
    254 j = 0;
    255 while (content[i] && pattern[j])
    256 {
    257 if (content[i] == pattern[j])
    258 {
    259 ++i;
    260 ++j;
    261 }
    262 else
    263 {
    264 j = next[j];
    265 if (-1 == j)
    266 {
    267 ++i;
    268 ++j;
    269 }
    270 }
    271 }
    272
    273 free(next);
    274
    275 if (pattern[j])
    276 {
    277 return NULL;
    278 }
    279 else
    280 {
    281 return &content[i - j];
    282 }
    283 }
    284
    285 int main(int argc, char * argv[])
    286 {
    287 printf("%s\n", kmp1(argv[1], argv[2]));
    288 printf("%s\n", kmp2(argv[1], argv[2]));
    289 printf("%s\n", kmp3(argv[1], argv[2]));
    290
    291 return 0;
    292 }

    BM算法:全称Boyer-Moore string search algorithm 预处理时间Θ(m + |Σ|) 匹配搜索时间Ω(n/m), O(n)

    代码
    1 using System;
    2 namespace stringsearch
    3 {
    4 /// <summary>
    5 /// 字符串搜索的基本抽象类
    6 /// </summary>
    7 public abstract class StringSearchTool
    8 {
    9 public enum Search
    10 {
    11 NOT_FOUND,
    12 SEARCH_EXACT,
    13 SEARCH_CASELESS
    14 }
    15
    16 protected Search search;
    17 protected String pattern;
    18
    19 public string Pattern
    20 {
    21 get
    22 {
    23 return pattern;
    24 }
    25 set
    26 {
    27 //大小写暂时无用处
    28 if (search == Search.SEARCH_CASELESS)
    29 {
    30 pattern = value;
    31 pattern.ToUpper();
    32 }
    33 else
    34 {
    35 pattern = value;
    36
    37 }
    38 }
    39 }
    40
    41 public StringSearchTool()
    42 {
    43 search = Search.SEARCH_CASELESS;
    44 pattern = null;
    45 }
    46 public StringSearchTool(string p)
    47 {
    48 search = Search.SEARCH_CASELESS;
    49 pattern = p;
    50
    51 }
    52
    53
    54 public StringSearchTool(string p, Search type)
    55 {
    56 search = type;
    57 pattern = p;
    58 }
    59 public int getPatternLength()
    60 {
    61 return pattern.Length;
    62 }
    63 public Search getSearchType()
    64 {
    65 return search;
    66 }
    67 public int find(string target)
    68 {
    69 return find(target, 0);
    70 }
    71 public abstract int find(string target, int start);
    72 }
    73 }
    74 // BoyerMoore算法
    75 using System;
    76 namespace stringsearch
    77 {
    78 /// <summary>
    79 ///
    80 /// </summary>
    81 public class BoyerMoore : stringsearch.StringSearchTool
    82 {
    83 protected int[] delta;
    84 private static readonly int DELTA_SIZE = 65536;
    85 public BoyerMoore()
    86 : base()
    87 {
    88
    89 }
    90 public BoyerMoore(string p)
    91 : base(p)
    92 {
    93
    94 }
    95 public BoyerMoore(string p, Search type)
    96 : base(p, type)
    97 {
    98
    99 }
    100
    101 public override int find(string target, int start)
    102 {
    103 if ((pattern == null) || (start < 0))
    104 return (int)Search.NOT_FOUND;
    105 String target2;
    106 //if(search==Search.SEARCH_CASELESS)
    107 // target2=target.ToUpper();
    108 //else
    109 target2 = target;
    110 int t = start + pattern.Length;
    111 while (t <= target2.Length)
    112 {
    113 int p = pattern.Length;
    114 while (pattern[p - 1] == target2[t - 1])
    115 {
    116 if (p > 1)
    117 {
    118 --p;
    119 --t;
    120 }
    121 else
    122 {
    123 return t - 1;
    124 }
    125 }
    126 t += delta[(int)target2[t - 1]];
    127 }
    128 return (int)Search.NOT_FOUND;
    129 }
    130 public new string Pattern
    131 {
    132 get
    133 {
    134 return base.Pattern;
    135 }
    136 set
    137 {
    138 base.Pattern = value;
    139 int n;
    140 delta = new int[DELTA_SIZE];
    141 for (n = 0; n < DELTA_SIZE; ++n)
    142 delta[n] = pattern.Length;
    143 for (n = 1; n < pattern.Length; ++n)
    144 delta[(int)pattern[n - 1]] = pattern.Length - n;
    145 delta[(int)pattern[pattern.Length - 1]] = 1;
    146 }
    147 }
    148 }
    149 }
    150 // 测试代码(部分):
    151 private void button1_Click(object sender, System.EventArgs e)
    152 {
    153 String terget = label1.Text;
    154 String pattern=textBox1.Text;
    155 BoyerMoore bm=new BoyerMoore();
    156 bm.Pattern=pattern;
    157 if (bm.find(terget,0)>0)
    158 MessageBox.Show(this,"你是不是在找 "+pattern+" ?"+"恭喜你找到了!");
    159 else
    160 MessageBox.Show(this,"下面的这段话中没有找到你所查找的文字!");
    161 }

    2. 有限模式集合匹配

    就是在字符串中查找多个子字符串的算法,常用于查找字典中的单词和一些脏字匹配算法

    Aho-Corasick算法:这是一种字典匹配算法,它用于在输入文本中查找字典中的字符串。时间复杂度是线性的。

    基本原理:该算法利用类似后缀树的方法构造一个trie结构,匹配时利用该结构来搜索。

    Aho-Corasick:可以认为是KMP算法在多串查找的扩展。先把所有要查找的串放在一起建一棵trie树。然后从源串的各个位置开始的串到trie树里查找。这样每次查找的复杂度为O(m),m为最长的子串。总共要查n次,所以复杂度为O(nm)。这只能说是brute force算法在多串下的扩展。所以还要在trie树里添加一些转移,使得源串在匹配过程中不出现回退。假设当前节点为i,源串匹配到字符c,如果节点i不存在c字母对应的转移。这时候应该跳转到一个节点j,从trie树根节点到这个节点的字母组成的字符串,应该是从根节点组成的字符串的后缀,如果有多个这样的节点,则跳转到最短的一个。分析一下这个自动机在匹配时的复杂度。要匹配过程中,源串不回退,所以遍历源串的复杂度为O(n)。但是在匹配失败的时候,会出现跳转,可能要跳转很多次才可以匹配成功。但是注意一点,每次跳转使得深度至少减1,而深度至多跟源串匹配的长度相等,所以可以跳转的次数不会超过源串的长度,所以总的复杂度是O(n)。

    要注意一个地方,后缀关系是可以传递的,a是b的后缀,b是c的后缀,则a是c的后缀。所以上面的表达式,最终是把一个串的后有后缀,从长到短串成了一个链表。

    难的地方就是自动机的构造。为了便于分析,把建trie树跟增加跳转分成两个过程。假设现在已经建好trie树。要为一个节点增加跳转,最简单的方法是,把根结点到它一路上的字符串组成的字符串,求后缀,再到trie树里查找,因为是求最长的后缀,所以从长到短,逐一去查找,直至找到为止,找到的节点就是要跳转的节点。这种方法的复杂度很高。观察一下,假设在trie树里,i节点是j节点的父结点,j的跳转结点的父结点表示的串,也是i的后缀。反过来说,根据一个节点的父结点的跳转可以快速地找到这个结点的跳转。假设当前节点的父结点的跳转结点为k,下一个要匹配的字符为c,看k是否有c的跳转,如果没有,则k继续跳转。会否出现没有找到合法的跳转,而k又不能再跳转的情况呢?初始的时候,把所有节点的跳转都指向根结点,可以避免这种情况。

    在实现的时候,因为跳转总是从深度大的结点跳到深度小的结点,所以对trie树作广度遍历。现在来分析一下构造的复杂度。trie树中结点的总数不会超过待查找的子串的长度和。但是每个结点的查找会进行多次跳转。分析一个子串它对应的一系列节点,每交跳转会使得深度减1,而这些节点的深点至多为子典的长度,所以一个子串对应的所有结点的构造费用之和,不会超过字串的长度,所以总的构造复杂度是跟字符长度和成正比的。


    Commentz-Walter 算法:是BM算法的自然扩展,它的速度并不快

    Rabin-Karp string search算法:该算法最差复杂度不好,因此运用的并不广泛。

    1. 问题描述

          给定目标字符串 T[0..n-1] (基于 0 的数组,数组长度为 n ),和模式串 P[0..m-1] ,问 P 可否匹配 T 中的任意子串,如果可以,返回匹配位置。

    2. 问题分析

    直观分析

          brute-force 的蛮力法,适用于较小规模的字符串匹配。

     优化

          主要介绍 3 种优化办法,分别具体为: Rabin-Karp 算法,有限自动机和 KMP 算法。将分为 3 篇博文分别讨论。本小节主要介绍 Rabin-Karp 算法。

     得出算法

          Rabin-Karp 算法(以下简称为 RK 算法),是基于这样的思路:即把串看作是字符集长度进制的数,由数的比较得出字符串的比较结果。例如,给定字符集为∑ ={0,1,2,3,4,5,6,7,8,9} ,∑长度为 d=10 ,那么任何以∑为字符集的串都可看作 d (此处为 10 )进制的数。

    记模式串 P[0..n-1] 对应的数值为 P , T[0..n-1] 所有长度为 m 的子串对应的数值为 ts ,设 P 和 T 都是基于字符集长度为 | ∑ |=d 的字符串。

    那么, ts 即为 T[s..s+m] 对应的数值,这里 0<=s<=n-m-1 。

    P = P[m]+d*(P[m-1]+d*(P[m-2]+..)))

    同样 t0 也可类似求得。

    最重要的是如何从 ts 求出 ts+1

    ts+1 =T[s+m]+d*(ts +dm-1 *T[s])

    注:此处是该算法的关键,即在常数时间内能够计算出下一个 m 长度的字串对应的数值。初看比较抽象,举个例子就比较明白了,设 x=12345 ,现在是已知长度为 3 的数值 234 ,现在要求 345 对应的数值,可以这样来得到: 345 = 5 + 10*(234-102 *2)

    3. 算法描述

          求出所有 m 长度子串所对应的数值,对数值进行比较,继而得出子串是否匹配。当模式串长度很大时,这时对应的数值会很大,比较起来比较麻烦,可使用对一个大奇数取模后进行比较。

    4. 具体实现

          这里实现的只是m值较小时的情形,大整数需要特定的类的支持(如可自定义大整数类),选取10进制的数是为了方便起见,当然字母也是OK的。

    代码
    #include "iostream"
    #include
    "string"
    #include
    "cmath"
    using namespace std;

    // get the value of the character in the set
    int getV(char p, string set)
    {
    for(int i=0; i<set.length(); i++)
    {
    if (p==set[i])
    return i;
    }
    return -1;
    }
    // d is the size of the character set
    int RK(string T, string P,string set)
    {
    int d = int(set.length());
    int n = T.length();
    int m = P.length();
    int h = pow(double(d), m-1);
    int p=0;
    int t = 0;
    for(int i=0; i<m; i++)
    {
    p
    = d*p + getV(P[i],set);
    t
    = d*t + getV(T[i], set);
    }
    for (int s=0; s<=n-m; s++)
    {
    cout
    <<"p,t is "<<p<<","<<t<<endl;
    if (p==t)
    return s;
    if (s<n-m)
    t
    = getV(T[s+m],set)+d*(t-h*getV(T[s],set));
    }
    return -1;
    }
    int main()
    {
    // set is the character set
    string set= "0123456789";
    // pattern P
    string P = "2365";
    // T is the string to match
    string T = "258569236589780";
    int i = RK(T, P, set);
    cout
    <<"the postition is:"<<i<<endl;
    return 0;
    }

  • 相关阅读:
    hadoop配置支持LZO压缩格式并支持分片
    CSS 图片:如何使用 CSS 来布局图片
    CSS3 用户界面:用户界面特性来调整元素尺寸,框尺寸和外边框
    CSS3 多列:如何将文本内容设计成像报纸一样的多列布局?
    mac Pycharm:如何使用anaconda安装jieba
    pycharm如何安装jieba词频统计器?
    CSS3 动画:使元素从一种样式逐渐变化为另一种样式的效果
    CSS3 过渡:用鼠标移过逐渐改变它原有样式
    CSS3 3D 转换:使用 3D 转换来对元素进行格式化
    CSS3 2D 转换:对元素进行移动、缩放、转动、拉长或拉伸
  • 原文地址:https://www.cnblogs.com/myphoebe/p/1794252.html
Copyright © 2011-2022 走看看