字符串匹配算法之有限自动机
-
代码分为两个个部分:
建立状态转移函数Transition函数:一个vector<map<char,int> >transition_map(m+1); 建立从数字到字符的对应关系,遍历P中每一个字符的每一种可能并找到K值; Matching_Prefix_Suffix:匹配前后缀://检验P[0...k-1]==P[q-k+1...q]是否匹配
匹配字符:没什么,就是匹配得K与m相等则匹配成功;
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<vector>
using namespace std;
bool Matching_Prefix_Suffix(char* P,int k,int q,char c);
vector<map<char,int> > Transition( char *P,const char* input_character);//状态转移函数的计算
void Finite_Automaton_Matcher(char* T,char* P,vector<map<char,int> >transition_map);
int main()
{
const char* input_character="abc"; //输入字母表
char T[]="abababacaba"; //文本串
char P[]="ababaca"; //模式串
vector<map<char,int> >transition_map=Transition(P,input_character);//char 为自动机中的字符 int 为转移函数值
Finite_Automaton_Matcher(T,P,transition_map);
return 0;
}
void Finite_Automaton_Matcher(char* T,char* P,vector<map<char,int> >transition_map)
{
int n=strlen(T); //文本串长度
int m=strlen(P); //模式串长度
int q=0; //转移函数的值
for(int i=0;i<n;i++){ //对于文本串中的每一个字符
q=transition_map[q][T[i]]; //迭代 前一个字符的转移函数值
if(q==m) //转移函数的值等于模式串的长度
printf("Pattern occurs with shift %d\n",i+1-m); //模式串的有效位移为i-m+1
}
}
bool Matching_Prefix_Suffix(char* P,int k,int q,char c)
{ //P为模式串 K为要验证的前缀和后缀的字符串长度
if(k==0) //q为当前自动机主线长度
return true; //k=0 空字符串 前缀和后缀肯定相等
if(k==1){ //只有一个字符串 证明自动机刚好开始创建
return P[0]==c; //如果模式串的第一个和其中的c相等 前缀等于后缀
}
return P[k-1]==c&&(!strncmp(P,P+q-k+1,k-1));//strncmp函数比较前k-1个数,也就是[0...k-2];匹配则返回0;
//检验P[0...k-1]==P[q-k+1...q]
}
vector<map<char,int> > Transition( char *P,const char* input_character)//计算转移函数的值
{
int m=strlen(P); //模式串的长度
int j=0,k;
printf("The main length of Finite_Automaton_Matcher is %d\n",m);
vector<map<char,int> >transition_map(m+1); //创建一个vector 一共有m+1个数据
for(int i=0;i<m;i++){ //对于模式串的长度
j=0;
while(input_character[j]!='\0'){ //对于输入串的每一种可能字符
k= min(m,i+1); //因为对于长度为i的字符串 它的转移函数最大值为i//数组下标从0开始 再加上后面k一来就减1 所以为i+2
//找到一个最大值k使得模式串的P[0...k]==P[...n-1]
while(!Matching_Prefix_Suffix(P,k,i,input_character[j])){
k=k-1;
}
transition_map[i][input_character[j]]=k;
j++;
}
}
for(int i=0;i<m;i++){
j=0;
while(input_character[j]!='\0'){
printf("<%d,%c>=%d\n",i,input_character[j],transition_map[i][input_character[j]]);
j++;
}
}
return transition_map; //返回一个vector 每一个元素为 map<char,int>
}
字符串匹配算法之KMP
-
代码分为两个部分:
建立求前缀函数get_pai: 对j有pai[j]=k;k:P[0 ~ k-1] == P[j-k ~ j-1],也就是前j个数中前缀与后缀匹配的最大长k; 匹配字符:如果S[i]!=P[j],则j=pai[j];
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void cal_next(string &str, vector<int> &next);
vector<int> KMP(string &str1, string &str2, vector<int> &next);
int main(int argc, char const *argv[])
{
vector<int> vec(20, 0);
vector<int> vec_test;
string str1 = "abababacaba";
string str2 = "ababaca";
vec_test = KMP(str1, str2, vec);
int k=vec_test.size();
for (int i=0;i<k;i++)
cout<<vec_test[i]<< endl;
return 0;
}
//部分匹配表
void cal_next(string &str, vector<int> &next)
{
const int len = str.size();
next[0] = -1;
int k = -1;
int j = 0;
while (j<len-1)
{
if (k==-1||str[j]==str[k])
{
++k;++j;
next[j]=k;//表示第j个字符有k个匹配(“最大长度值” 整体向右移动一位,然后初始值赋为-1)
}
else
k = next[k];//往前回溯
}
}
vector<int> KMP(string &str1, string &str2, vector<int> &next)
{
vector<int> vec;
cal_next(str2, next);
int i = 0;//i是str1的下标
int j = 0;//j是str2的下标
int str1_size = str1.size();
int str2_size = str2.size();
while (i < str1_size && j < str2_size)
{
//如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),
//都令i++,j++. 注意:这里判断顺序不能调换!
if (j == -1 || str1[i] == str2[j]){++i;++j;}
else j = next[j];//当前字符匹配失败,直接从str[j]开始比较,i的位置不变
if (j == str2_size)//匹配成功
{
vec.push_back(i - j);//记录下完全匹配最开始的位置
j = -1;//重置
}
}
return vec;
}