/**************************************************************************
* 本文件主要包含了字符串匹配的一些算法,这些算法可以根据实际情况在不同的
* 场景中应用。
*
* 字符串匹配问题就是在文本串T中找到模式串P出现的所有位置,就朴素算法而言,
* 在文本串和模式串的长度比较短时使用,它的时间复杂度较高,但实现以及使用比
* 较简单,而且不需要额外存储空间。
*
* 当文本串比较长时,可以使用Knuth-Morris-Pratt算法,它优化了比较失败后回退
* 的问题,从而使得时间复杂度可以下降到O(n)的级别,但是它需要额外的存储空间
* 和预处理过程。
*
* Boyer-Moore算法很类似KMP算法,它与KMP的不同体现在模式串比较顺序和遇到不
* 匹配字符右移的距离所计算的方法不同,在一般情况下,它的效率要比KMP算法高
* 几倍,但是在文本串和模式串具有周期性的情况下却会退化到O(n^2),例如,当文
* 本为ababababa,模式串为ababa时。此外,它需要的更多的存储空间。对原始的
* BM算法进行优化,可以使它在最坏情况下的时间复杂度也为O(n)。
*
* Sunday算法也是类似于KMP、BM的一种算法,它在一般情况下的效率要比KMP和BM
* 好,但是在极端情况下同BM算法一样会退化到朴素算法的级别,这跟该算法的具体
* 实现有关。对原始Sunday算法进行优化,也可以使它在最坏情况下的时间复杂度降
* 为O(n)。
*
* 以上的这些字符串匹配算法都只能解决一个模式串的问题,而Rabin-Karp算法可以
* 解决多于一个模式串的问题,但是它的使用也是有很大的局限性的,在模式串长度
* 很大的情况下不适宜使用,HASH算法也是影响执行效率的关键因素。
*
* Aho-Corasick算法是基于有限自动机的多模式串匹配算法,这种算法是基于Trie
* 的,它可以解决在一段文本中查找指定的多个模式出现的位置,该算法了构造一
* 个有限自动机,从而对文本串只需要进行一次遍历即可。AC算法的时间复杂度与构
* 造有限自动机的时间和遍历文本串的时间相关。
**************************************************************************/
#ifndef STRING_H_
#define STRING_H_
/**************************************************************************
* 朴素算法查找字符串,时间复杂度是O(nm),不需要预处理
**************************************************************************/
int naive_search(const char * txt, const char * pat, int offset[])
{
int i, j, n, m, c = 0;
n = strlen(txt);
m = strlen(pat);
for (i = 0; i <= n - m; i++) {
for (j = 0; j < m; j++)
if (pat[j] != txt[i + j])
break;
if (j == m)
offset[c++] = i;
}
return c;
}
/**************************************************************************
* Knuth-Morris-Pratt算法查找字符串,预处理时间是O(m),时间复杂度是O(n)
**************************************************************************/
int kmp_search(const char * txt, const char * pat, int offset[])
{
int i, j, n, m, c = 0;
n = strlen(txt);
m = strlen(pat);
int * next = new int[m];
for (i = 1, j = 0, next[0] = 0; i < m; i++){
j = next[i - 1];
while (j > 0 && pat[j] != pat[i])
j = next[j - 1];
if (pat[j] == pat[i])
j++;
next[i] = j;
}
for(i = 0, j = 0; i < n; i++) {
while (j > 0 && txt[i] != pat[j])
j = next[j - 1];
if(txt[i] == pat[j])
j++;
if (j == m)
offset[c++] = i - j + 1;
}
delete [] next;
return c;
}
/**************************************************************************
* Boyer-Moore算法查找字符串,预处理时间是Θ(m + |Σ|),时间复杂度是Ω(n/m),
* O(mn),它在一般情况下比KMP要快。
**************************************************************************/
int bm_search(const char * txt, const char * pat, int offset[])
{
int i, j, n, m, a, b, c = 0;
n = strlen(txt);
m = strlen(pat);
int * bc = new int[256];
for(i = 0; i < 256; i++)
bc[i] = m;
for (i = 0; i <= m - 1; i++)
bc[(unsigned char)pat[i]] = m - i - 1;
int * sfx = new int[m];
sfx[m - 1] = m;
for (i = m - 2; i >= 0; i--) {
j = i;
while(pat[j] == pat[m - i - 1 + j] && --j >= 0);
sfx[i] = i - j;
}
int * gc = new int[m];
for (i = 0; i < m; i++)
gc[i] = m;
for (i = m - 1; i >= 0; i--)
if (sfx[i] == i + 1)
for (j = 0; j < m - i - 1; ++j)
if (gc[j] == m)
gc[j] = m - i - 1;
for (i = 0; i <= m - 2; ++i)
gc[m - 1 - sfx[i]] = m - i - 1;
for (i = 0; i <= n - m; i += a > b ? a : b) {
j = m - 1;
while(j >= 0 && pat[j] == txt[j + i]) j--;
if (j < 0) {
offset[c++] = i;
a = b = 1;
} else {
a = gc[j];
b = bc[(unsigned char)txt[j + i]] - m + j + 1;
}
}
delete [] sfx; delete [] gc; delete [] bc;
return c;
}
/**************************************************************************
* Boyer-Moore-Galil 算法查找字符串,预处理时间是Θ(m + |Σ|),时间复杂度是
* Ω(n/m), O(n),它在一般情况下比KMP要快。
**************************************************************************/
int bmg_search(const char * txt, const char * pat, int offset[])
{
int i, j, n, m, o, a, b, c = 0;
n = strlen(txt);
m = strlen(pat);
int * bc = new int[256];
for(i = 0; i < 256; i++)
bc[i] = m;
for (i = 0; i <= m - 1; i++)
bc[(unsigned char)pat[i]] = m - i - 1;
int * sfx = new int[m];
sfx[m - 1] = m;
for (i = m - 2; i >= 0; i--) {
j = i;
while(pat[j] == pat[m - i - 1 + j] && --j >= 0);
sfx[i] = i - j;
}
int * gc = new int[m];
for (i = 0; i < m; i++)
gc[i] = m;
for (i = m - 1; i >= 0; i--)
if (sfx[i] == i + 1)
for (j = 0; j < m - i - 1; ++j)
if (gc[j] == m)
gc[j] = m - i - 1;
for (i = 0; i <= m - 2; ++i)
gc[m - 1 - sfx[i]] = m - i - 1;
for (i = 0, o = gc[0]; o > 0 && i <= m - 2; ++i)
if (gc[i] != (i / o + 1) * o) o = 0;
for (i = 0, j = m - 1; i <= n - m; i += a > b ? a : b) {
while(j >= 0 && pat[j] == txt[j + i]) j--;
if (j < 0) {
offset[c++] = i;
a = b = o ? o : 1;
j = o ? o : m - 1;
} else {
a = gc[j];
b = bc[(unsigned char)txt[j + i]] - m + j + 1;
j = m - 1;
}
}
delete [] sfx; delete [] gc; delete [] bc;
return c;
}
/**************************************************************************
* Sunday算法查找字符串,预处理时间是Θ(m + |Σ|),时间复杂度是Ω(n/m),
* O(mn),它在一般情况下比KMP、BM要快.
**************************************************************************/
int sunday_search(const char * txt, const char * pat, int offset[])
{
int i, j, n, m, c = 0;
n = strlen(txt);
m = strlen(pat);
int * next = new int[256];
for (i = 0; i < 256; i++)
next[i] = m + 1;
for (i = 0; i < m; i++)
next[(unsigned char)pat[i]] = m - i;
for (i = 0, j = 0; i <= n - m;) {
for (j = 0; j < m; j++)
if (txt[i + j] != pat[j])
break;
if (j == m)
offset[c++] = i;
i += next[(unsigned char)txt[i + m]];
}
return c;
}
/**************************************************************************
* Rabin-Karp算法查找字符串,时间复杂度最坏是O((n-m+1)m),预处理
* 时间复杂度为Θ(m)。
**************************************************************************/
int rk_search(const char * txt, const char * pat, int offset[])
{
unsigned int i, j, p, t, h, n, m, c = 0;
const unsigned int d = 256, q = 16777213;
n = strlen(txt);
m = strlen(pat);
p = (unsigned char)pat[0];
t = (unsigned char)txt[0];
for (i = 1, h = 1; i < m; i++) {
p = (d * p + (unsigned char)pat[i]) % q;
t = (d * t + (unsigned char)txt[i]) % q;
h = (h * d) % q;
}
for (i = 0; i <= n - m; i++) {
if (p == t) {
for (j = 0; j < m; j++)
if (txt[i + j] != pat[j])
break;
if (j == m)
offset[c++] = i;
}
t = (t + q - (unsigned char)txt[i] * h % q) % q;
t = (d * t % q + (unsigned char)txt[i + m]) % q;
}
return c;
}
/**************************************************************************
* Aho-Corasick算法查找字符串,时间复杂度是O(n),需要预处理
**************************************************************************/
typedef struct TrieNode{
char c;
int father;
int son[26];
int next;
int flag;
int count;
}TrieNode;
int trie_cnt = 0;
TrieNode trie[1000] = {0, {0}, 0, 0};
inline unsigned int ac_char2index(char c)
{
return c - 'a';
}
void ac_add_string(const char * sub)
{
int i, j, k, l;
for (i = 0, j = 0; sub[j] != 0; j++)
{
k = ac_char2index(sub[j]);
if (trie[i].son[k] > 0)
{
i = trie[i].son[k];
}
else
{
l = ++trie_cnt;
trie[i].son[k] = l;
trie[l].father = i;
trie[l].c = sub[j];
i = l;
}
}
trie[i].flag = 1;
}
void ac_build_automation()
{
int i, j, k, l;
for (i = 1; i <= trie_cnt; i++)
{
j = trie[i].father;
k = trie[j].next;
l = ac_char2index(trie[i].c);
if (j > 0 && trie[k].son[l] > 0)
trie[i].next = trie[k].son[l];
}
}
int ac_search(const char * txt, int offset[])
{
int i, j, k, l, n, c;
n = strlen(txt);
for (i = 0, j = 0, c = 0; i < n; i++) {
l = ac_char2index(txt[i]);
while (!trie[j].son[l] && j)
j = trie[j].next;
j = trie[j].son[l];
for (k = j; k; k = trie[k].next)
if (trie[k].flag)
offset[c++] = i;
}
return c;
}
#endif
/*********************************文件结尾********************************/