sunday、kmp 从匹配串的左边往右处理
bm、 horspool 从匹配串的右边往左处理
一般是某位字符失配时, 主串指针后移一位,继续处理;各个算法核心就是确定主串指针后移长度,移动的次数越少、长度越长就能越快
匹配出结果
规定:主串txt,匹配串pat,要在txt中寻找与pat匹配的字符串;失配时,主串失配字符Tc,位置Ta,匹配串失配字符Pc,位置Pa,
位置均从0开始,位置值为-1表示相应的字符(串)不存在, pat的长度strlen(pat);
sunday:根据紧跟在pat之后的txt中的字符在pat中的位置addr确定移动距离,
主串指针 移动距离等于strlen(pat) - addr
kmp:根据pat中Pa左侧已匹配的字符串的最长前缀prefix来确定pat指针的移动位置
pat指针的移动位置是prefix的下一个位置
bm:Tc在pat中距pat末尾最近的距离为len1,pat中Pa右侧已匹配的字符串的好前缀的位置为addr,
则坏字符badlen = len1, 好前缀goodlen = strlen(pat) - 1 - addr + strlen(pat) - 1 - Pa,
主串指针移动距离等于 badlen和goodlen中较大的那个
horspool:跟据Tc在pat中Pa左侧最近的出现位置addr确定移动距离,
主串指针 移动距离等于Pa- addr
标准KMP:
KMP:给出两个字符串A(称为主串)和B(称为模板串),长度分别为lenA和lenB,要求在线性时间内,对于每个A[i](0<=i<lenA),求出A[i]往前和B匹配 的最大匹配长度,记为ex[i](或者说,ex[i]为满足A[i-z+1..i]==B[0..z-1]的最大的z值)。KMP的主要目的是求B是不是A的子串,以及若是,B在 A 中所有出现的位置(当ex[i]=lenB时)。
【算法】
设next[i]为满足B[i-z+1..i]==B[0..z-1]的最大的z值(也就是B的自身匹配)。设目前next[0..lenB-1]与ex[0..i-1]均已求出,要用它们来求ex[i]的值。
扩展KMP:扩展kmp是求模式串和主串的每一个后缀的最长公共前缀
给出主A串和模板串B,长度分别为lenA和lenB,要求在线性时间内,对于每个A[i](0<=i<lenA),求出A[i..lenA-1]与B的最长公共前缀长度,记为ex[i](或者说,ex[i] 为满足A[i..i+z-1]==B[0..z-1]的最大的z值)。扩展KMP可以用来解决很多字符串问题,如求一个字符串的最长回文子串和最长重复子串。
【算法】
设next[i]为满足B[i..i+z-1]==B[0..z-1]的最大的z值(也就是B的自身匹配)。设目前next[0..lenB-1]与ex[0..i-1]均已求出,要用它们来求ex[i]的值。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
// 暴力搜索
int StrSearch(const char *str, const char *pat)
{
int i = 0, j = 0, len1, len2;
if (!str || !pat)
return -1;
len1 = strlen(str);
len2 = strlen(pat);
if (len2 <= 0)
return -1;
while(i < len1 && j < len2) {
if(str[i] == pat[j]) {
++i;
++j;
}
else {
i = i - j + 1;
j = 0;
}
}
if(j >= len2)
return i - len2;
return -1;
}
// Sunday字符串匹配算法
int SundayStrSearch(const char *str, const char *pat)
{
int i, j, len1, len2, *next;
if (!str || !pat)
return -1;
len1 = strlen(str);
len2 = strlen(pat);
if (len2 <= 0)
return -1;
next = (int *)malloc(256 * sizeof(int));
if (!next)
return -1;
for (i = 0; i < 256; i++)
next[i] = len2 + 1;
for (i = 0; i < len2; i++)
next[(unsigned char)pat[i]] = len2 - i; // 记录字符到最右段的最短距离 + 1
i = 0, j = 0;
while (i < len1 && j < len2) {
if (str[i] == pat[j]) {
i++;
j++;
}
else {
int tmp = i + len2 - j;
if (tmp >= len1) {
i = len1;
break;
}
i = i - j + next[(unsigned char)str[tmp]];
j = 0;
}
}
free(next);
if (j >= len2)
return i - len2;
return -1;
}
// 失配时,pat的下一跳位置(pat = "abab"; value[] = {-1, 0, -1, 0};)
int *Next(char const* pat, int len)
{
int i = 0, j = -1;
int *value = (int *)malloc(len * sizeof(int));
if (!value)
return NULL;
value[0] = -1;
while (i < len - 1) {
if (j == -1 || pat[i] == pat[j]) {
i++;
j++;
value[i] = pat[i] == pat[j] ? value[j] : j;
}
else
j = value[j];
}
return value;
}
// Kmp字符串匹配
int KmpStrSearch(const char *str, const char *pat)
{
int len1;
int len2;
int *next, i, j;
if (!str || !pat)
return -1;
len1 = strlen(str);
len2 = strlen(pat);
if (len2 <= 0)
return -1;
next = Next(pat, len2);
if (!next)
return -1;
i = 0, j = 0;
while(i < len1 && j < len2) {
if(j == -1 || str[i] == pat[j]) {
i++;
j++;
}
else {
j = next[j];
}
}
free(next);
if(j >= len2)
return i - j;
return -1;
}
// 坏字符数组: 某一坏字符距离末尾的长度; 出现多次的字符以最靠右的为准
int *BadArray(const unsigned char *pat)
{
int len;
int *bad;
if (!pat)
return NULL;
bad = (int *)malloc(256 * sizeof(int));
if (!bad)
return NULL;
len = strlen((const char *)pat);
for (int i = 0; i < 256; i++)
bad[i] = len;
for (int i = 0; i < len; i++)
bad[pat[i]] = len - 1 - i;
return bad;
}
// 好后缀数组构造
int *GoodArray(const char *pat)
{
int len, *good;
char endc;
const char *cut, *prev, *next, *tmp;
if (!pat)
return NULL;
len = strlen(pat);
if (len <= 0)
return NULL;
good = (int *)malloc(len *sizeof(int));
if (!good)
return NULL;
good[len - 1] = 1; // 好后缀数组的最后元素值为1
endc = pat[len - 1]; // 结尾字符
cut = pat + len - 2;
for(int i = len - 2; i >= 0; i--, cut--) {
tmp = pat + len - 2;
while (1) {
int matchflag = 1;
while (tmp >= pat && *tmp != endc)
tmp--;
prev = tmp;
tmp = tmp >= pat ? tmp - 1 : tmp;
next = pat + len - 1;
// 分三种情况:
// case1:找不到匹配的好后缀(完全或部分匹配)
// pat = "abcd"; cut = 1; 末尾的"d"无匹配字符
// case2:找到完全匹配的好后缀
// pat = "abab"; cut = 1; 末尾的"ab"与开头的"ab"完全匹配
// case3:找到部分匹配的好后缀
// pat = "abba"; cut = 1; 末尾的"a"与开头的"a"部分匹配(完全匹配应该是"ba")
if(prev < pat) // case1,无匹配字符;case3,部分匹配(循环回来到此)
break;
while(prev > pat && next > cut + 1) {
prev--, next--;
if(*prev != *next) {
matchflag = 0;
break;
}
}
if(!matchflag) // 未完全匹配,继续往前找
continue;
// case2,完全匹配
if( prev <= pat || *(prev - 1) != *(next - 1))
break;
}
good[i] = len - 1 - i + next - prev;
}
return good;
}
// Bm字符串匹配算法
int BmStrSearch(const char *str, const char *pat)
{
int i, j, len1, len2, *bad, *good;
if (!str || !pat)
return - 1;
bad = BadArray((const unsigned char *)pat);
if (!bad)
return -1;
good = GoodArray(pat);
if (!good) {
free(bad);
return -1;
}
len1 = strlen(str);
len2 = strlen(pat);
if (len2 <= 0)
return -1;
i = len2 - 1;
j = i;
while (i < len1 && j >= 0) {
int goodskip, badskip;
if (str[i] == pat[j]) {
i--;
j--;
}
else {
badskip = bad[(unsigned char)str[i]];
goodskip = good[j];
i += (goodskip > badskip ? goodskip : badskip);
j = len2 - 1;
}
}
free(bad);
free(good);
if (i < len1)
return i + 1;
return -1;
}
// Horspool字符串匹配算法
int HorspoolStrSearch(const char *str, const char *pat)
{
int len1, len2, i, j;
if (!str || !pat)
return -1;
len1 = strlen(str);
len2 = strlen(pat);
if (len2 <= 0)
return -1;
i = len2 - 1;
j = i;
while (i < len1 && j >= 0) {
if (str[i] == pat[j]) {
i--;
j--;
}
else {
// pat串中找到失配字符str[i],计算主串指针移动距离
while (j >= 0 && pat[j] != str[i])
j--;
i = i + len2 - 1 - j;
j = len2 - 1;
}
}
if(i < len1)
return i + 1;
return -1;
}
int main(int argc, char **argv)
{
int ret;
ret = StrSearch(argv[1], argv[2]);
printf("1: %s\n", (ret >= 0 ? argv[1] + ret : NULL));
ret = SundayStrSearch(argv[1], argv[2]);
printf("2: %s\n", (ret >= 0 ? argv[1] + ret : NULL));
ret = KmpStrSearch(argv[1], argv[2]);
printf("3: %s\n", (ret >= 0 ? argv[1] + ret : NULL));
ret = BmStrSearch(argv[1], argv[2]);
printf("4: %s\n", (ret >= 0 ? argv[1] + ret : NULL));
ret = HorspoolStrSearch(argv[1], argv[2]);
printf("5: %s\n", (ret >= 0 ? argv[1] + ret : NULL));
int *shift;
shift = GoodArray(argv[2]);
for (int i = 0, len = strlen(argv[2]); i < len; i++)
printf("%d ", shift[i]);
printf("\n");
return 0;
}