Shift-And 和 KMP一样都是基于前缀搜索的方法,复杂度为O(n);
《柔性字符串匹配》:
"只有当模式串小于8时,KMP才比基于后缀和基于子串的搜索方法有效。而在这个范围内,Shift-And算法和Shift-Or算法能够在所有机器上运行,速度至少是Knuth-Morris-Pratt的两倍,并且更易于实现。"
Shift-And对字母表中的每个字符构造一个位序列,根据下面的公式更新匹配状态D:
D = ((D<<1) | 0^{m-1}1) & B[t_{i+1}];
B[t_{i+1}]是字符t_{i+1}的位序列,当D的第j位为1并且t_{i+1}与p_{j+1}相等时,D的第j+1位更新为1,上式中的位或是因为空串也是文本的后缀。
因为状态表中位序列的有效长度为模式串的长度,所以当模式串长度超过机器字长时,需要构造自定义类型(应该行得通)。
示例:
# include <stdio.h> # include <string.h> int b[128]; /* int型共32位,模式串不超过32位 */ int shift_and(char *p, char *t); int shift_or(char *p, char *t); int main() { char s1[5000], s2[30]; freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); while (~scanf("%s%s", s1, s2)) { printf("shirt-and : %d, shift-or : %d.\n", shift_and(s2, s1), shift_or(s2, s1)); } return 0; } int shift_and(char *p, char *t) { int n, m, i, d, f; n = strlen(p); m = strlen(t); memset(b, 0, sizeof(b)); for (i = 0; i < n; ++i) { b[p[i]] |= 0x1<<i; } d = 0; f = 0x1 << (n-1); for (i = 0; i < m; ++i) { d = ((d<<1) | 0x1) & b[t[i]]; if (d & f) return i-n+1; /* 可以根据需要返回第一次匹配结果 */ } return -1; } int shift_or(char *p, char *t) { int n, m, i, d, f; n = strlen(p); m = strlen(t); memset(b, 0xff, sizeof(b)); for (i = 0; i < n; ++i) { b[p[i]] &= ~(0x1<<i); } d = 0xff; f = 0x1 << (n-1); for (i = 0; i < m; ++i) { d = (d<<1) | b[t[i]]; /* 可以根据需要返回第一次匹配结果 */ if (!(d&f)) return i-n+1; } return -1; }