java 查找多个字符串_java – 在一个庞大的集合中查找两个字符串的所有连接

给定一组50k字符串,我需要找到所有对(s,t),这样s,t和s t都包含在这个集合中.

我试过的

,还有一个额外的约束:s.length()> = 4&& t.length()> = 4.这使得可以将字符串分组为长度为4的前缀,并分别为后缀.然后对于每个长度至少为8的字符串,我使用组合的前四个字符和使用其最后四个字符的t候选集查找s的候选集.这有效,但需要查看30M候选对(s,t)以找到7k结果.

这个令人惊讶的大量候选人来自这样一个事实,即字符串是来自有限词汇表的(主要是德语)单词,并且单词的开头和结尾通常是相同的.它仍然比尝试所有2.5G对要好得多,但比我希望的要糟糕得多.

我需要的

由于附加约束可能会被删除而且集合会增长,我正在寻找更好的算法.

“失踪”的问题

有人抱怨我不问问题.所以缺少的问号在下一句的末尾.如何更有效地完成这项工作,理想情况下不使用约束?

解决方法:

算法1:测试对,而不是单打

一种方法是,不是从所有可能的对到包含这些对的所有可能的复合字符串,而是从所有可能的复合字符串开始工作,看它们是否包含对.这将问题从n ^ 2次查找(其中n是字符串的数量> = 4个字符)更改为m * n次查找(其中m是所有字符串的平均长度> = 8个字符,减去7,n是现在字符串的数量> = 8个字符).这是一个实现:

int minWordLength = 4;

int minPairLength = 8;

Set strings = Stream

.of(

"a", "abc", "abcdef", "def", "sun", "sunshine", "shine",

"bear", "hug", "bearhug", "cur", "curlique", "curl",

"down", "downstream", "stream"

)

.filter(s -> s.length() >= minWordLength)

.collect(ImmutableSet.toImmutableSet());

strings

.stream()

.filter(s -> s.length() >= minPairLength)

.flatMap(s -> IntStream

.rangeClosed(minWordLength, s.length() - minWordLength)

.mapToObj(splitIndex -> ImmutableList.of(

s.substring(0, splitIndex),

s.substring(splitIndex)

))

.filter(pair ->

strings.contains(pair.get(0))

&& strings.contains(pair.get(1))

)

)

.map(pair ->

pair.get(0) + pair.get(1) + " = " + pair.get(0) + " + " + pair.get(1)

)

.forEach(System.out::println);

给出结果:

downstream = down + stream

如上所示,这具有m * n的平均算法复杂度.所以实际上是O(n).在最坏的情况下,O(n ^ 2).有关算法复杂性的更多信息,请参见hash table.

说明

>将所有字符串长四个或更多字符放入一个哈希集(这对于搜索来说平均O(1)复杂度).为方便起见,我使用了Guava的ImmutableSet.用你喜欢的任何东西.

> filter:仅限制长度为八个或更多字符的项目,表示我们的候选项是列表中另外两个单词的组合.

> flatMap:对于每个候选人,计算所有可能的子词对,确保每个子词长度至少为4个字符.由于可能有多个结果,这实际上是一个列表列表,因此将其展平为单个深度列表.

> rangeClosed:生成所有整数,表示将在我们要检查的对中的第一个单词中的字符数.

> mapToObj:使用与我们的候选字符串组合的每个整数来输出两个项目的列表(在生产代码中,您可能想要更清晰的东西,如双属性值类或适当的现有类).

> filter:仅限于两者都在列表中的对.

>地图:稍微调整一下结果.

> forEach:输出到控制台.

算法选择

该算法被调整为比列表中的项目数短的单词.如果列表非常短并且单词很长,那么切换回合成任务而不是分解任务会更好.鉴于列表大小为50,000个字符串,而德语单词长度不太可能超过50个字符,这是1:1000因素支持此算法.

另一方面,如果您有50个字符串,平均长度为50,000个字符,则不同的算法效率会更高.

算法2:排序并保留候选列表

我想了一会儿的一个算法是对列表进行排序,知道如果一个字符串表示一对的开头,那么可能是其中一对的所有候选字符串将紧接在它之后,在集合中以该字符串开头的项目.对我上面的棘手数据进行排序,并添加一些混淆因素(下行,下行,下调),我们得到:

a

abc

abcdef

bear

bearhug

cur

curl

curlique

def

down ---------\

downs |

downer | not far away now!

downregulate |

downstream ---/

hug

shine

stream

sun

sunshine

因此,如果要保留所有要检查的项目的运行集合,我们可以在每个单词的基本上恒定的时间内找到候选复合,然后直接探测到剩余单词的哈希表:

int minWordLength = 4;

Set strings = Stream

.of(

"a", "abc", "abcdef", "def", "sun", "sunshine", "shine",

"bear", "hug", "bearhug", "cur", "curlique", "curl",

"down", "downs", "downer", "downregulate", "downstream", "stream")

.filter(s -> s.length() >= minWordLength)

.collect(ImmutableSet.toImmutableSet());

ImmutableList orderedList = strings

.stream()

.sorted()

.collect(ImmutableList.toImmutableList());

List candidates = new ArrayList<>();

List> pairs = new ArrayList<>();

for (String currentString : orderedList) {

List nextCandidates = new ArrayList<>();

nextCandidates.add(currentString);

for (String candidate : candidates) {

if (currentString.startsWith(candidate)) {

nextCandidates.add(candidate);

String remainder = currentString.substring(candidate.length());

if (remainder.length() >= minWordLength && strings.contains(remainder)) {

pairs.add(new AbstractMap.SimpleEntry<>(candidate, remainder));

}

}

}

candidates = nextCandidates;

}

pairs.forEach(System.out::println);

结果:

down=stream

这个算法的复杂性有点复杂.我认为搜索部分是O(n)平均值,O(n ^ 2)最坏情况.最昂贵的部分可能是排序 – 这取决于所使用的算法和未排序数据的特征.所以用一粒盐,但它有可能.在我看来,这比从庞大的数据集中构建Trie要便宜得多,因为您只需要对其进行一次全面的探测,并且不会对构建成本进行任何摊销.

此外,这次我选择了Map.Entry来保持这对.你怎么做是完全随意的.制作自定义Pair类或使用一些现有的Java类就可以了.

标签:java,algorithm,string-algorithm

来源: https://codeday.me/bug/20190527/1161814.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值