Algorithm 如何最佳地返回一个短语是否出现在一个词流中?

Algorithm 如何最佳地返回一个短语是否出现在一个词流中?,algorithm,data-structures,stream,Algorithm,Data Structures,Stream,有一个字流来了,这是显着大。随着单词的不断出现,可以要求它判断一个短语是否出现在已经看到的流中?在不同的时间可能有多个这样的查询 例如,假设到目前为止看到的词流是: 你好,世界这里是另一个程序员 然后,它被要求判断短语这里是否有另一个被看到,这在本例中是正确的 如何以最佳方式返回此文件 我一直在尝试使用图形构造和在查询时执行BFS来解决此问题,但它带来了两个问题: 首先,为了达到最佳效果,我还必须将图对中节点的words=>address存储在哈希表中 第二,当存在循环时,算法失败,如流中所示

有一个字流来了,这是显着大。随着单词的不断出现,可以要求它判断一个短语是否出现在已经看到的流中?在不同的时间可能有多个这样的查询

例如,假设到目前为止看到的词流是:

你好,世界这里是另一个程序员

然后,它被要求判断短语
这里是否有另一个
被看到,这在本例中是正确的

如何以最佳方式返回此文件

我一直在尝试使用图形构造和在查询时执行BFS来解决此问题,但它带来了两个问题:

  • 首先,为了达到最佳效果,我还必须将图对中节点的words=>address存储在哈希表中

  • 第二,当存在循环时,算法失败,如流中所示:
    abcdabce


为需求提出最佳解决方案。

您可以查找“后缀树的在线构造”,并找到Ukkonen的算法,该算法处理流,并且在处理每个字符后始终为流准备后缀树,如果您已经看到n个字符,则运行时间和空间为O(n)。然后,每次给您一个查询短语时,您都可以使用后缀树的子字符串匹配算法来查找给定查询短语的所有匹配项,如果您的查询短语的长度为m,则查询时间是最佳的O(m)来查找匹配项。

因为您接收的文本正文是以流式方式搜索的,因此,使用“预处理”文本以实现更高效的搜索。下面是C#中的一个高效实现,它以流式方式处理要搜索的文本

static IEnumerable<int> Search(string text, string query)
{
    var D = new Dictionary<int, int>();
    //Loop invariant: D[i] == j iff text[i..(i+j)] == query[0..j]
    //                for all pairs (i,j) in D
    for (int i = 0; i < text.Length; i++)
    {
        foreach (var k in D.Keys.ToList())
        {
            D[k] = D[k] + 1;
            if (D[k] == query.Length)
            {
                yield return k;
                D.Remove(k);
            }
            else if (text[i] != query[D[k]])
            {
                D.Remove(k);
            }
        }
        if (text[i] == query[0])
            D.Add(i, 0);
    }
    foreach (var k in D.Keys)
    {
        if (D[k] == query.Length)
            yield return k;
    }
}
静态IEnumerable搜索(字符串文本、字符串查询)
{
var D=新字典();
//循环不变量:D[i]==j iff文本[i..(i+j)]==query[0..j]
//对于D中的所有对(i,j)
for(int i=0;i
基于流的版本可以如下实现。我认为流结束的情况可能处理得不好,但是你应该能够将这个想法应用到一些即使在边缘情况下也有效的事情上

class SearcherState
{
    public Dictionary<int, int> D = new Dictionary<int, int>();
    public int i = 0;
}

static Func<char, int?> Searcher(string query)
{
    var state = new SearcherState();
    return c =>
    {
        int? result = null;
        foreach (var k in state.D.Keys.ToList())
        {
            state.D[k] = state.D[k] + 1;
            if (state.D[k] == query.Length)
            {
                result = k;
                state.D.Remove(k);
            }
            else if (c != query[state.D[k]])
            {
                state.D.Remove(k);
            }
        }
        if (c == query[0])
            state.D.Add(state.i, 0);
        state.i++;
        return result;
    };
}
类搜索状态
{
公共字典D=新字典();
公共整数i=0;
}
静态函数搜索器(字符串查询)
{
var state=新的SearcherState();
返回c=>
{
int?结果=null;
foreach(state.D.Keys.ToList()中的变量k)
{
state.D[k]=state.D[k]+1;
if(state.D[k]==query.Length)
{
结果=k;
状态D.移除(k);
}
else if(c!=query[state.D[k]]
{
状态D.移除(k);
}
}
if(c==查询[0])
州D.Add(州i,0);
state.i++;
返回结果;
};
}

< OzzieGooen >,你会认为这些解决方案中的任何一个都是最优的吗?@你提到的问题是,尽管你可能发现相关的,完全不同,因为它要求报告只能用HASH-Table处理的副本。因为你不知道查询短语会是什么,如果只有一个查询,那么它就没有意义了。假设你有一个数据流,你必须记住到目前为止看到的所有数据。或者,当数据流还在输入时,你可能会在不同的时间得到多个查询?我猜这是我能理解的。或者,你可能想在数据流输入时做一些类似连续线性时间的工作,这样你可以更快地解决问题n当查询进入时为否?这至少需要ω(n)到目前为止,流已产生n个字符的时间。对于大n和相当小长度的查询,每次查询时都有n个字符是不可接受的。@user2566092提出的问题是,要搜索的文本正在流式传输,并在流式传输时进行搜索。例如,您不能提前构建trie。Furthermore,似乎只指定了一个查询字符串。OP没有说会有多个查询。imo根据问题的编写方式,在不同的时间可以有多个查询。但是如果只有一个查询,并且用户不介意在查询到来时等待一段时间以获得答案,即使我同意你的回答是可以接受的。在我看来,真正的问题是流的速度有多快,这样就可以确定在线线性时间预处理是否能够跟上,然后使查询在进入时基本上不花时间。@user2566092请参阅关于如何利用问题的在线性质,我回答的第二部分是。你创建一个
var searcher=searcher(“这是另一个”);
,然后每次从文本流中输入新字符时,你都会反复调用
searcher(c)
。如果
searcher(c)
返回的不是
null
,这意味着在返回的索引中找到了匹配项。首先提供脱机
搜索
方法的目的是在显示联机版本之前,显示相同的算法是如何以脱机方式执行的。@TimothyShields+1用于代码。很抱歉,如果您搞错了,但是,我的问题是,当流进来时,可能会提出疑问。这就是为什么我要尝试t