【字符串系列】柔性字符串匹配代码实现

来源:互联网 发布:windows pro n 编辑:程序博客网 时间:2024/05/02 17:21
http://www.cnblogs.com/jackiesteed/articles/2865457.html

柔性字符串匹配, 介绍各种字符串匹配算法, 用来学习字符串算法不错.

下面是我自己用C++实现的算法代码, 陆续贴上来...

ShiftAnd算法:

复制代码
#include <iostream>#include <fstream>#include <algorithm>#include <cstring>using namespace std;typedef unsigned long long ULL;//ShiftAnd算法//做一些假定: 传入的模式串和text串都是小写字母.//为了减少一些复杂度.// 整形最长64位, 所以模式串最长支持64// 返回-1说明没有找到, 否则返回匹配的开始位置.int ShiftAnd(char pat[], char text[]){    ULL B[26];    int len_pat = strlen(pat);    if(len_pat > 64)    {        cout << "模式串长度超过64, 运算不支持..." << endl;        return -2;    }    //这里是初始化B数组的过程    memset(B, 0, sizeof(B));    for(int i = 0; i < 26; i++)    {        for(int j = 0; j < len_pat; j++)        {            if(pat[j] == i + 'a')            {             B[i] |= ULL(1LL << j);            }        }    }    ULL D = 0;    int len_text = strlen(text);    for(int i = 0; i < len_text; i++)    {        //这个式子是关键,         //掩码D的第j + 1位是活跃的, 当且仅当D的j是活跃的(p[1...j]是t[1...i]的后缀), 而且p[j + 1] == t[i + 1]        // !!!!!记住一个意义, 就是D的第x是1, 说明pat的前x位是t[1...i]的后缀..        D = ((D << 1) | 1LL) & B[text[i] - 'a'];        if((D & ULL(1LL << (len_pat - 1))) != 0)        {            return i - len_pat + 1;        }    }    return -1;}
复制代码

ShiftOr算法:

复制代码
#include <iostream>#include <fstream>#include <algorithm>#include <climits>#include <cstdlib>using namespace std;typedef unsigned long long ULL;//ShiftOr算法//做一些假定: 传入的模式串和text串都是小写字母.//为了减少一些复杂度.// 整形最长64位, 所以模式串最长支持64// 返回-1说明没有找到, 否则返回匹配的开始位置.void BinaryDump(ULL x){    for(int i = 0; i < 64; i++)    {        if(x & ULL(1LL << (63 - i))) cout << 1;        else cout << 0;    }    cout << endl;}int ShiftOr(char pat[], char text[]){    int len_pat = strlen(pat);    if(len_pat > 64)    {        cout << "模式串长度超过64, 运算不支持..." << endl;        return -2;    }    ULL B[26];    //memset(B, 0, sizeof(B));    for(int i = 0; i < 26; i++)        B[i] = 0;    //cout << B[0] << endl;    for(int i = 0; i < 26; i++)    {        for(int j = 0; j < len_pat; j++)        {            if(pat[j] == i + 'a')            {                B[i] |= ULL(1LL << j);            }        }    }        //这里获得B的反码.    for(int i = 0; i < 26; i++)    {        B[i] ^= (-1LL);    }    ULL D = ULL(-1LL);    int len_text = strlen(text);    for(int i = 0; i < len_text; i++)    {        D = (D << 1) | B[text[i] - 'a'];        if((D & ULL(1LL << (len_pat - 1))) == 0)            return i - len_pat + 1;    }    return -1;}
复制代码

 Horspool算法:

复制代码
int Horspool(char pat[], char text[]){    int len_pat = strlen(pat);    int len_text = strlen(text);    if(len_pat > len_text) return -1;    int d[256];    for(int i = 'a'; i <= 'z'; i++)    {        d[i] = len_pat;    }    for(int i = 0; i < len_pat - 1; i++)    {        d[pat[i]] = len_pat - i - 1;    }    int pos = 0;    while(pos <= len_text - len_pat)    {        int i = len_pat - 1;        while(i >= 0 && text[pos + i] == pat[i]) i--;        if(i == -1)            return pos;        pos += d[text[pos + len_pat - 1]];    }    return -1;}
复制代码

 后缀数组

参考链接: http://blog.csdn.net/jokes000/article/details/7839686

复制代码
#include <iostream>#include <fstream>#include <cstring>using namespace std;const int MAXN = 100000;int rank[MAXN];int sa[MAXN];char str[MAXN];bool change_abc(int r[], char str[]){    int idx = 0;    for(; str[idx] != '\0'; idx ++)    {        if(str[idx] >= 'A' && str[idx] <= 'Z') r[idx] = str[idx] - 64;        else if(str[idx] >= 'a' && str[idx] <= 'z') r[idx] = str[idx] - 70;        else return false;    }    return true;}bool cmp(int r[], int i, int j, int l){    return r[i] == r[j] && r[i + l] == r[j + l];}//m是字典里面字符的个数//n是长度//sa是最终的suffixarray这个数组void suffix_array(int rank[], int sa[], int n, int key_num){    int count[MAXN], i, secondkey[MAXN], len, *_secondkey = secondkey, *_rank = rank, *tmp, p;    //count用于计数排序    //len表示倍增法里面每次的子串长度, 不断*2    for(int i = 0; i <= key_num; i++)count[i] = 0;    for(int i = 0; i < n; i++) count[_rank[i]]++;    for(int i = 1; i <= key_num; i++) count[i] += count[i - 1];    for(int i = n - 1; i >= 0; i--)sa[--count[_rank[i]]] = i;    //到这里, sa[i] 表示该位置是目前可以确定的第i大后缀的位置.    //默认算在后面的更大一些    //rank表示, 第i位置的后缀应该排第几, 跟sa是对应的.    //_secondkey表示对第二关键字的排序    for(p = 1, len = 1; p < n; len *= 2, key_num = p)    {        //下面这两行是对于第二关键字的排序        //为什么要把n-j到n-1这几个放到最前面呢? 因为他们后面没有数据, 当做空来算, 最小        for(p = 0, i = n - 1; i >= n - len; i--, p++) _secondkey[p] = i;        for(i = 0; i < n; i++) if(sa[i] >= len) _secondkey[p++] = sa[i] - len;        //count是一个中间数组, 里面的内容的意思大致是, 对key的计数, 然后用这个计数来排序, 可以理解为是使用了计数排序        //下面这4行就是典型的基数排序了, 可以看出来跟函数最开始的初始化差不多        for(i = 0; i <= key_num; i++) count[i] = 0;        for(i = 0; i < n; i++) count[_rank[_secondkey[i]]] ++;        //_rank[_secondkey[i]], 基数排序        for(i = 1; i <= key_num; i++) count[i] += count[i - 1];        for(i = n - 1; i >= 0; i--) sa[--count[_rank[_secondkey[i]]]] = _secondkey[i];        //_rank表示第一个关键字,        //也表示算法里面描述的那个rank数组        //下面需要比较, 是因为有些相邻的是相同的串        for(tmp = _rank, _rank = _secondkey, _secondkey = tmp, _rank[sa[0]] = 0, i = 1, p = 1; i < n; i++)        {            _rank[sa[i]] = cmp(_secondkey, sa[i], sa[i - 1], len) ? p - 1: p++;        }    }}int main(){//    freopen("input.txt", "r", stdin);    cin >> str;    int len = strlen(str) + 1;    change_abc(rank, str);    suffix_array(rank, sa, len, 52);    for(int i = 1; i < len; i++)    {        cout << sa[i] << " ";    }    cout << endl;    for(int j = 1; j < len; j++)        printf("%s\n", str + sa[j]);    return 0;}
复制代码

 BNDM算法, 子串匹配

复制代码
#include <iostream>#include <fstream>#include <algorithm>#include <cstring>#include <climits>#include <cstdio>using namespace std;typedef unsigned long long ULL;const int MAXN = 300;//BNDM算法, 全名Backward Nondeterministic Dawg Matching, 来自柔性字符串匹配, 是一个子串匹配算法, 利用并行运算.//做一些假定: 传入的模式串和text串都是小写字母.//为了减少一些复杂度.// 整形最长64位, 所以模式串最长支持64// 返回-1说明没有找到, 否则返回匹配的开始位置.//-2说明模式串超过了64长度, 暂不支持.//为了简化, 只支持小写字母..int BNDM(char pat[], char text[]){    int len_pat = strlen(pat);    if(len_pat > 64)    {        cout << "模式串长度超过64, 运算不支持..." << endl;        return -2;    }    int len_text = strlen(text);    ULL B[MAXN] = {0};    for(int i = 0; i < len_pat; i ++)    {        B[int(pat[i])] |= ULL(1LL << (len_pat - i - 1)); //为什么用len_pat -i -1? 因为是要利用pat的反转的串    }    int pos = 0;    while(pos <= len_text - len_pat)    {        int idx = len_pat - 1;        int last = len_pat - 1;        ULL mask = ULL(-1LL); //初始化成1111111,表示模式串的每一个位置都能与空串相匹配..        while(mask != 0LL)        {            mask = mask & B[int(text[pos + idx])];            idx -= 1;            if((mask & ULL(1LL << (len_pat - 1))) != 0LL) //如果进行与运算结果不是0,                                                          // 那么说明目前取到的这一个子串是模式串的前缀, len_pat - idx是前缀长度,                                                          //如果idx ==0, 那么就完全匹配到了..            {                if(idx >= 0) last = idx; //last用来移动窗口                else return idx + pos + 1;//如果idx==0, 那说明已经完全匹配了.            }            mask <<= 1;            //mask的某一位是1, 就说明, 从这1位开始的len_pat-1-idx的长度的串, 是同当前从text里读入的串相同..        }        pos += last + 1;    }    return -1;}int main(){    char pat[] = "a";    char text[] = "aaaaaaabcddd";    cout << BNDM(pat, text) << endl;    return 0;}
复制代码

 

0 0
原创粉丝点击