对于我们需要查找字符串的子串数的问题,其中每个字符的重复次数应该相同。给定字符串仅包含3个字符(A,B,C)。
我只能用O(n ^ 2)的算法来制作它。
for(;i<len;i++)
{
if(s[i]=='A')
ac++;
else if(s[i]=='B')
bc++;
else if(s[i]=='C')
cc++;
for(k=i+1;k<len;k++)
{
if(s[k]=='A')
ac++;
else if(s[k]=='B')
bc++;
else if(s[k]=='C')
cc++;
if(ac==bc && bc==cc)
{count++;}
}
ac=0;bc=0;cc=0;
}
计算更长的字符串需要很长时间(范围为10 ^ 5)。请帮助我们找到更好的解决方案。
答案 0 :(得分:1)
如果你愿意在C中实现哈希映射,你可以用我认为的O(n)来解决这个问题。
保持所有As和B的标准化计数。归一化,我的意思是C计数总是为零。 C计数也是隐含的,因为您的字符串只包含As,Bs和Cs,因此当前字符串长度必须为A + B + C.
从(0,0)计数的计数为1的哈希映射开始。
通过字符串一次。传递A时,增加A计数。传递B时,增加B计数。传递C时,减少A和B计数。将当前计数(A,B)添加到哈希映射中(如果它不存在并递增)。
举例说明:
0 0
A 1 0 *--------------
B 1 1 valid
A 2 1 substring
C 1 0 *--------------
A 2 0
最后,遍历所有哈希映射条目,并将该条目的三角形总和添加到总计数中。通过三角形总和t(n)
,我的意思是:t(1) = 0
,t(2) = 1
,t(3)= 2 + 1
,t(4)= 3 + 2 + 1
,依此类推。这反映了哈希条目表示有效子串的边界以及您可以合并相邻子串的事实:
ACB ABC BCA BCA +4
ACBABC ABCBCA BCABCA +3
ACBABCBCA ABCBCABCA +2
ACBABCBCABCA +1
在伪C中:
int nsubseq(const char *str)
{
Map *map = map_new();
const char *p;
int aa = 0;
int bb = 0;
map_add(map, key(aa, bb), 1);
for (p = str; *p; p++) {
if (*p == 'A') aa++;
else if (*p == 'B') bb++;
else if (*p == 'C') aa--, bb--;
int *q = map_find(map, key(aa, bb));
if (q) {
*q = *q + 1;
} else {
map_add(map, key(aa, bb), 1);
}
}
int count = 0;
for (int *p = map_begin(map); p; p = map_next(map)) {
int n = *p;
while (n--) count += n;
}
map_delete(map);
return count;
}
(这实际上是真正的C,只是你必须实现所有map
函数。或者当然使用现有的哈希映射实现。)
代码的性能取决于哈希映射实现,但是如果散列大小为4096,我可以在不到一秒的时间内扫描一百万个均衡分布的As,Bs和Cs的字符串。这是optmum案例;性能下降是一个字符串分布不均匀。没有命中的角落情况(仅As或仅As和Bs),基本上每个角色在哈希映射中创建一个新条目,大约需要十二倍。
如果哈希的条目是索引而不是计数的列表,你甚至可以提取子串的值,尽管对于一个包含100k条目的字符串,这将是过度的。