你还没有给出一个大小为m的字符包B(multiset)和一个大小为n的字符串文本S.是否有可能在线性时间O(n)中找到S中可由B(4!= 24种组合)创建的所有子串?
示例:
S = abdcdbcdadcdcbbcadc (n=19)
B = {b, c, c, d} (m=4)
Result: {cdbc (Position 3), cdcb (Position 10)}
我发现最快的解决方案是为每个角色保留一个计数器,并在每个步骤中将其与Bag进行比较,因此运行时间为O(n * m)。如果需要,可以显示算法。
答案 0 :(得分:4)
有一种方法可以在O(n)中进行,假设我们只对长度为m的子串感兴趣(否则这是不可能的,因为对于包含字符串中所有字符的包,你必须返回s的所有子串,表示无法在O(n)中计算的O(n ^ 2)结果。
算法如下:
将行李转换为直方图:
hist = []
for c in B do:
hist[c] = hist[c] + 1
初始化我们要修改的正在运行的直方图(histrunsum是histrun中的字符总数):
histrun = []
histrunsum = 0
我们需要两个操作:在直方图中添加一个字符并将其删除。它们的运作方式如下:
add(c):
if hist[c] > 0 and histrun[c] < hist[c] then:
histrun[c] = histrun[c] + 1
histrunsum = histrunsum + 1
remove(c):
if histrun[c] > 0 then:
histrun[c] = histrun[c] - 1
histrunsum = histrunsum + 1
本质上,histrun捕获当前子字符串中B中存在的字符数量。如果histrun等于hist,则我们的子字符串与B具有相同的字符。histrun等于hist iff histrunsum等于B的长度。
现在将前m个字符添加到histrun;如果histrunsum等于B的长度;发出第一个子串;现在,直到我们到达字符串的结尾,删除当前子字符串的第一个字符并添加下一个字符。
add,remove是O(1),因为hist和histrun是数组;检查hist是否等于histrun是通过将histrunsum与长度(B)进行比较来完成的,所以它也是O(1)。循环迭代计数为O(n),结果运行时间为O(n)。
答案 1 :(得分:1)
感谢您的回答。必须更改add()
和remove()
方法才能使算法正常运行。
add(c):
if hist[c] > 0 and histrun[c] < hist[c] then
histrunsum++
else
histrunsum--
histrun[c] = histrun[c] + 1
remove(c):
if histrun[c] > hist[c] then
histrunsum++
else
histrunsum--
histrun[c] = histrun[c] - 1
说明: histrunsum可以看作两个多重集合的相同程度。
add(c):当histrun multiset中的char出现次数少于hist multiset时,该char的额外出现必须“奖励”,因为histrun multiset越来越接近hist multiset。如果已经在histrun集合中至少有相同或更多的字符,则额外的字符为负数。
删除(c):与add(c)类似,其中当在thetrun multiset中的数字时,对char的删除进行加权。 hist multiset。
示例代码(PHP):
function multisetSubstrings($sequence, $mset)
{
$multiSet = array();
$substringLength = 0;
foreach ($mset as $char)
{
$multiSet[$char]++;
$substringLength++;
}
$sum = 0;
$currentSet = array();
$result = array();
for ($i=0;$i<strlen($sequence);$i++)
{
if ($i>=$substringLength)
{
$c = $sequence[$i-$substringLength];
if ($currentSet[$c] > $multiSet[$c])
$sum++;
else
$sum--;
$currentSet[$c]--;
}
$c = $sequence[$i];
if ($currentSet[$c] < $multiSet[$c])
$sum++;
else
$sum--;
$currentSet[$c]++;
echo $sum."<br>";
if ($sum==$substringLength)
$result[] = $i+1-$substringLength;
}
return $result;
}
答案 2 :(得分:0)
使用散列。对于multiset中的每个字符,分配一个UNIQUE素数。通过将与数字相关联的素数乘以该数字的频率,计算任何字符串的哈希值。
示例:CATTA。设C = 2,A = 3,T = 5.哈希= 2 * 3 * 5 * 5 * 3 = 450
散列多重集(将其视为字符串)。现在浏览输入字符串,并计算长度为k的每个子字符串的散列(其中k是多集中的字符数)。检查此哈希是否与多集哈希匹配。如果是,那就是这样的事情。
可以在线性时间内非常容易地计算哈希值,如下所示:
让multiset = {A,A,B,C},A = 2,B = 3,C = 5.
Multiset hash = 2 * 2 * 3 * 5 = 60
让text = CABBAACCA
(i)CABB = 5 * 2 * 3 * 3 = 90
(ii)现在,下一个字母是A,丢弃的字母是第一个,C。所以新的哈希=(90/5)* 2 = 36
(iii)现在,A被丢弃,A也被添加,所以新的哈希=(36/2)* 2 = 36
(iv)现在丢弃B,并添加C,因此hash =(36/3)* 5 = 60 = multiset hash。因此,我们发现了一个这样的必然发生 - BAAC
这个程序显然需要O(n)时间。