在我的程序中,我需要搜索一个相当小的子串(<1 kb)的相当大的字符串(~1 mb)。 问题是字符串包含“a?c”意义上的简单通配符,这意味着我想搜索“abc”或“apc”等字符串,...(我只对第一次出现感兴趣)。 / p>
到目前为止,我使用了简单的方法(这里是伪代码)
algorithm "search", input: haystack(string), needle(string)
for(i = 0, i < length(haystack), ++i)
if(!CompareMemory(haystack+i,needle,length(needle))
return i;
return -1; (Not found)
其中“CompareMemory”返回0 iff第一个和第二个参数相同(也与通配符有关)仅与第三个参数给出的字节数有关。
我现在的问题是,如果有一个快速算法(你不必给它,但如果你这样做,我宁愿选择c ++,c或伪代码)。我开始了here 但我认为大多数快速算法都不允许使用通配符(顺便说一下它们利用字符串的性质)。
我希望问题的格式是正确的,因为我是新来的,请提前谢谢!
答案 0 :(得分:3)
一种快速的方式,与使用正则表达式相同(我建议无论如何),是找到固定在针上的东西,“a”,但不是“?”,并搜索它,然后看看你是否完全匹配。
j = firstNonWildcardPos(needle)
for(i = j, i < length(haystack)-length(needle)+j, ++i)
if(haystack[i] == needle[j])
if(!CompareMemory(haystack+i-j,needle,length(needle))
return i;
return -1; (Not found)
正则表达式会产生类似于此的代码(我相信)。
答案 1 :(得分:1)
在c字符的字母表中的字符串中,让S具有长度s并且让T_1 ... T_k具有平均长度b。将搜索S个目标字符串中的每一个。 (问题陈述没有提到对给定字符串的多次搜索;我在下面提到它,因为在该范例中我的程序运行良好。)
程序使用O(s + c)时间和空间进行设置,并且(如果S和T_i是随机字符串)O(k * u * s / c)+ O(k * b + k * b * s / c ^ u)搜索的总时间,如图所示在程序中u = 3。对于更长的目标,你应该增加,并选择罕见的,广泛分离的关键字符。
在步骤1中,程序创建一个s + TsizMax整数的数组L(在程序中,TsizMax =允许的目标长度)并将其用于下一次出现字符的位置的c列表,列表头在H []和T []中的尾巴。这是O(s + c)时间和空间步骤。
在步骤2中,程序重复读取并处理目标字符串。步骤2A选择u = 3个不同的非通配字符(在当前目标中)。如图所示,程序只使用前三个这样的字符;通过更多的工作,它可以改为使用目标中最稀有的字符来提高性能。请注意,它不能处理少于三个此类字符的目标。
线“L [T [r]] = L [g + i] = g + i;”在步骤2A中,在L中设置具有适当增量偏移的保护单元,使得步骤2G将在搜索结束时自动执行,而不需要在搜索期间进行任何额外测试。 T [r]为字符r索引列表的尾部单元格,因此单元格L [g + i]成为字符r的新的自引用列表结尾。 (这种技术允许循环以最少的无关条件测试运行。)
步骤2B将变量a,b,c设置为列表头部位置,并设置与目标中所选关键字符之间的距离对应的增量dab,dac和dbc。
步骤2C检查关键字符是否出现在S中。此步骤是必要的,因为否则步骤2E中的while循环将挂起。我们不希望在循环中进行更多检查,因为它们是搜索的内部循环。
步骤2D执行步骤2E到2i,直到var c指向S的结尾,此时不可能再进行匹配。
步骤2E由u = 3 while循环组成,“强制增量距离”,即,只要它们不是模式兼容的,就将爬行索引a,b,c相互叠加。 while循环相当快,每个实质上(删除了++ si仪器)“while(v + d&lt; w)v = L [v]”用于各种v,d,w。复制三个while循环几次可能会略微提高性能,并且不会改变净结果。
在步骤2G中,我们知道u个关键字符匹配,因此我们将目标与匹配点进行完全比较,并进行野性字符处理。步骤2H报告比较结果。所给出的计划也报告了本节中的不匹配;在生产中删除它。
步骤2I推进所有关键字符索引,因为当前索引字符都不是另一个匹配的关键部分。
您可以运行该程序以查看一些操作计数统计信息。例如,输出
Target 5=<de?ga>
012345678901234567890123456789012345678901
abc1efgabc2efgabcde3gabcdefg4bcdefgabc5efg
@ 17, de?ga and de3ga match
@ 24, de?ga and defg4 differ
@ 31, de?ga and defga match
Advances: 'd' 0+3 'e' 3+3 'g' 3+3 = 6+9 = 15
表示步骤2G输入3次(即关键字符匹配3次);完全比较成功了两次;步骤2E while循环高级索引6次;步骤2I高级索引9次;总共有15个进步,用于搜索42个字符的字符串以获得目标。
/* jiw
$Id: stringsearch.c,v 1.2 2011/08/19 08:53:44 j-waldby Exp j-waldby $
Re: Concept-code for searching a long string for short targets,
where targets may contain wildcard characters.
The user can enter any number of targets as command line parameters.
This code has 2 long strings available for testing; if the first
character of the first parameter is '1' the jay[42] string is used,
else kay[321].
Eg, for tests with *hay = jay use command like
./stringsearch 1e?g a?cd bc?e?g c?efg de?ga ddee? ddee?f
or with *hay = kay,
./stringsearch bc?e? jih? pa?j ?av??j
to exercise program.
Copyright 2011 James Waldby. Offered without warranty
under GPL v3 terms as at http://www.gnu.org/licenses/gpl.html
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
//================================================
int main(int argc, char *argv[]) {
char jay[]="abc1efgabc2efgabcde3gabcdefg4bcdefgabc5efg";
char kay[]="ludehkhtdiokihtmaihitoia1htkjkkchajajavpajkihtijkhijhipaja"
"etpajamhkajajacpajihiatokajavtoia2pkjpajjhiifakacpajjhiatkpajfojii"
"etkajamhpajajakpajihiatoiakavtoia3pakpajjhiifakacpajjhkatvpajfojii"
"ihiifojjjjhijpjkhtfdoiajadijpkoia4jihtfjavpapakjhiifjpajihiifkjach"
"ihikfkjjjjhijpjkhtfdoiajakijptoik4jihtfjakpapajjkiifjpajkhiifajkch";
char *hay = (argc>1 && argv[1][0]=='1')? jay:kay;
enum { chars=1<<CHAR_BIT, TsizMax=40, Lsiz=TsizMax+sizeof kay, L1, L2 };
int L[L2], H[chars], T[chars], g, k, par;
// Step 1. Make arrays L, H, T.
for (k=0; k<chars; ++k) H[k] = T[k] = L1; // Init H and T
for (g=0; hay[g]; ++g) { // Make linked character lists for hay.
k = hay[g]; // In same loop, could count char freqs.
if (T[k]==L1) H[k] = T[k] = g;
T[k] = L[T[k]] = g;
}
// Step 2. Read and process target strings.
for (par=1; par<argc; ++par) {
int alpha[3], at[3], a=g, b=g, c=g, da, dab, dbc, dac, i, j, r;
char * targ = argv[par];
enum { wild = '?' };
int sa=0, sb=0, sc=0, ta=0, tb=0, tc=0;
printf ("Target %d=<%s>\n", par, targ);
// Step 2A. Choose 3 non-wild characters to follow.
// As is, chooses first 3 non-wilds for a,b,c.
// Could instead choose 3 rarest characters.
for (j=0; j<3; ++j) alpha[j] = -j;
for (i=j=0; targ[i] && j<3; ++i)
if (targ[i] != wild) {
r = alpha[j] = targ[i];
if (alpha[0]==alpha[1] || alpha[1]==alpha[2]
|| alpha[0]==alpha[2]) continue;
at[j] = i;
L[T[r]] = L[g+i] = g+i;
++j;
}
if (j != 3) {
printf (" Too few target chars\n");
continue;
}
// Step 2B. Set a,b,c to head-of-list locations, set deltas.
da = at[0];
a = H[alpha[0]]; dab = at[1]-at[0];
b = H[alpha[1]]; dbc = at[2]-at[1];
c = H[alpha[2]]; dac = at[2]-at[0];
// Step 2C. See if key characters appear in haystack
if (a >= g || b >= g || c >= g) {
printf (" No match on some character\n");
continue;
}
for (g=0; hay[g]; ++g) printf ("%d", g%10);
printf ("\n%s\n", hay); // Show haystack, for user aid
// Step 2D. Search for match
while (c < g) {
// Step 2E. Enforce delta distances
while (a+dab < b) {a = L[a]; ++sa; } // Replicate these
while (b+dbc < c) {b = L[b]; ++sb; } // 3 abc lines as many
while (a+dac > c) {c = L[c]; ++sc; } // times as you like.
while (a+dab < b) {a = L[a]; ++sa; } // Replicate these
while (b+dbc < c) {b = L[b]; ++sb; } // 3 abc lines as many
while (a+dac > c) {c = L[c]; ++sc; } // times as you like.
// Step 2F. See if delta distances were met
if (a+dab==b && b+dbc==c && c<g) {
// Step 2G. Yes, so we have 3-letter-match and need to test whole match.
r = a-da;
for (k=0; targ[k]; ++k)
if ((hay[r+k] != targ[k]) && (targ[k] != wild))
break;
printf ("@ %3d, %s and ", r, targ);
for (i=0; targ[i]; ++i) putchar(hay[r++]);
// Step 2H. Report match, if found
puts (targ[k]? " differ" : " match");
// Step 2I. Advance all of a,b,c, to go on looking
a = L[a]; ++ta;
b = L[b]; ++tb;
c = L[c]; ++tc;
}
}
printf ("Advances: '%c' %d+%d '%c' %d+%d '%c' %d+%d = %d+%d = %d\n",
alpha[0], sa,ta, alpha[1], sb,tb, alpha[2], sc,tc,
sa+sb+sc, ta+tb+tc, sa+sb+sc+ta+tb+tc);
}
return 0;
}
请注意,如果您比当前首选答案更喜欢此答案,请取消标记该答案并标记此答案。 :)
答案 2 :(得分:0)
我认为正则表达式通常使用finite state automation-based search。尝试实现它。