我想合并相似的字符串(单词)(字符串在其他字符串中)。
word
wor
words
wormhole
hole
会:
words
wormhole
当wor
与:word
重叠时,words
,wormhole
- wor
将被丢弃;
word
重叠:words
- word
被丢弃;
hole
重叠:wormhole
- hole
被丢弃;
但words
,wormhole
不重叠 - 所以他们留下来
我怎样才能做到这一点?
修改
我的解决方案是:
while read a
do
grep $a FILE |
awk 'length > m { m = length; a = $0 } END { print a }'
done < FILE |
sort -u
但我不知道它是否会导致大型数据集出现问题。
答案 0 :(得分:3)
在Ruby中:
list = %w[word wor words wormhole]
list.uniq
.tap{|a| a.reverse_each{|e| a.delete(e) if (a - [e]).any?{|x| x.include?(e)}}}
答案 1 :(得分:3)
使用足够长的单词列表,对单词的任何嵌套循环都会非常缓慢。我就是这样做的:
use strict;
use warnings;
use File::Slurp 'read_file';
chomp( my @words = read_file('/usr/share/dict/words') );
my %overlapped;
for my $word (@words) {
$word =~ /(.*)(?{++$overlapped{$1}})(*FAIL)/;
--$overlapped{$word};
}
print "$_\n" for grep ! $overlapped{$_}, @words;
或许可以通过Darshan Computing建议处理最长到最短的单词来改进它。
答案 2 :(得分:2)
您可以使用哈希来计算单词列表的子字符串:
use strict;
use warnings;
use feature 'say';
my %seen; # seen substrings
my @words; # original list
while (<DATA>) { # read a new substring
chomp;
push @words, $_; # store the original
while (length) { # while a substring remains
$seen{$_}++; # increase its counter
chop; # shorten the substring
}
}
# All original words with count == 1 are the merged list
my @merged = grep $seen{$_} == 1, @words;
say for @merged;
__DATA__
w
word
wor
words
wormhole
hole
holes
<强>输出:强>
words
wormhole
holes
当然,您需要补偿大小写,标点符号和空格,因为哈希键是精确的,而键Foo
与键foo
不同。
答案 3 :(得分:1)
将列表理解与any
/ all
:
>>> lis = ['word','wor', 'words', 'wormhole']
#all
>>> [x for x in lis if all(x not in y for y in lis if y != x)]
['words', 'wormhole']
#any
>>> [x for x in lis if not any(x in y for y in lis if y != x)]
['words', 'wormhole']
您也可以在此处使用marisa_trie:
>>> import marisa_trie
>>> lis = ['word','wor', 'words', 'wormhole', 'hole', 'holes']
>>> def trie(lis):
trie = marisa_trie.Trie(lis)
return [x for x in lis if len(trie.keys(unicode(x))) ==1 ]
...
>>> trie(lis)
['words', 'wormhole', 'holes']
答案 4 :(得分:1)
按升序对所有单词列表进行排序。如果一个单词是 下一个字的子串,丢弃当前字;继续前进。
...需要O(n log n)进行排序,我不确定Ashwini解决方案的时间复杂度,但它看起来要超过O(n log n)。
我认为这是一个O(n)解决方案......
from collections import defaultdict
words = ['word', 'wor', 'words', 'wormhole']
infinite_defaultdict = lambda: defaultdict(infinite_defaultdict)
mydict = infinite_defaultdict()
for word in words:
d = mydict
for char in word:
d = d[char]
result = []
for word in words:
d = mydict
for char in word:
d = d[char]
if not d:
result.append(word)
print result
...打印......
['words', 'wormhole']
<强>更新强>
但我不知道它是否会导致大型数据集出现问题。
为了比较,使用来自/usr/share/dict/words
的10,000个单词,这需要大约70毫秒的CPU时间,而Ashwini需要大约11秒。
更新2
好。原始问题看起来好像单词在开始时只能重叠,但如果它们可以在任何地方重叠,则此代码将不起作用。我认为任何能够做到这一点的算法都会出现O(n²)的最坏情况复杂性。
答案 5 :(得分:1)
我理解你的问题是
给定一个单词列表,我们想要删除所有那些是其他单词的子串的单词。
这是Perl的一般解决方案:
sub weed_out {
my @out;
WORD:
while (my $current = shift) {
for (@_) {
# skip $current word if it's a substring of any other word
next WORD if -1 != index $_, $current;
}
push @out, $current;
}
return @out;
}
请注意shift
参数数组中的@_
,因此内部循环每次都会变短。
如果我们在执行内循环时遇到一个单词$current
字的子字符串,我们实际上可以通过splice
删除它:
WORD:
while (my $current = shift) {
for (my $i = 0; ; $i++) {
last unless $i <= $#_; # loop condition must be here
# remove the other word if it's a substring of $current
splice(@_, $i, 1), redo if -1 != index $current, $_[$i];
# skip $current word if it's a substring of any other word
next WORD if -1 != index $_[$i], $current;
}
push @out, $current;
}
但我宁愿对“优化”进行基准测试。
如果需要,可以很容易地将其嵌入到shell脚本中:
$ perl - <<'END' FILE
my @words = <>;
chomp(@words);
WORD: while (my $current = shift @words) {
for (@words) {
# skip $current word if it's a substring of any other word
next WORD if -1 != index $_, $current;
}
print "$current\n";
}
END
答案 6 :(得分:1)
在我看来,排序最长到最短的单词,然后我们可以只通过排序列表一次,只匹配保留的单词。我在算法分析方面很差,但这对我来说很有意义,我认为表现会很好。它似乎也有效,假设保留字的顺序无关紧要:
words = ['word', 'wor', 'words', 'wormhole', 'hole']
keepers = []
words.sort_by(&:length).reverse.each do |word|
keepers.push(word) if ! keepers.any?{|keeper| keeper.include?(word)}
end
keepers
# => ["wormhole", "words"]
如果保留的单词的顺序很重要,那么修改它就可以很容易。一种选择就是:
words & keepers
# => ["words", "wormhole"]
答案 7 :(得分:1)
使用awk
:
awk '
NR==FNR {
a[$1]++
next
}
{
for (x in a) {
if (index ($1,x) == 0) {
a[x]
}
else {
delete a[x]
a[$1]
}
}
}
END {
for (x in a) {
print x
}
}' inputFile inputFile
inputFile of:
word
wormholes
wor
words
wormhole
hole
Returns:
words
wormholes
答案 8 :(得分:1)
bash解决方案:
#!/bin/bash
dict="word wor words wormhole hole "
uniq=()
sort_by_length() {
for word; do
printf "%d %s\n" ${#word} "$word"
done | sort -n | cut -d " " -f2-
}
set -- $(sort_by_length $dict)
while [[ $# -gt 0 ]]; do
word=$1
shift
found=false
for w; do
if [[ $w == *"$word"* ]]; then
found=true
break
fi
done
if ! $found; then
uniq+=($word)
fi
done
echo "${uniq[@]}"
答案 9 :(得分:1)
冗长的perl oneliner,
perl -nE 'chomp;($l,$p)=($_,0); @w=grep{ $p=1 if /$l/; $p|| $l!~/$_/} @w; $p or push @w,$l}{say for @w' file