给定一组字符串,例如:
EFgreen
EFgrey
EntireS1
EntireS2
J27RedP1
J27GreenP1
J27RedP2
J27GreenP2
JournalP1Black
JournalP1Blue
JournalP1Green
JournalP1Red
JournalP2Black
JournalP2Blue
JournalP2Green
我希望能够检测到这些是三组文件:
有没有任何已知的方法来解决这个问题 - 我可以阅读任何已发表的论文吗?
我正在考虑的方法是每个字符串查看所有其他字符串并查找常见字符和不同字符的位置,尝试查找最常见的字符串集,但我担心这不是非常有效并且可能会给出误报。
请注意,这与'How do I detect groups of common strings in filenames'不同,因为它假定字符串后面总是有一系列数字。
我会从这里开始:http://en.wikipedia.org/wiki/Longest_common_substring_problem
外部链接中有补充信息的链接,包括文章中解释的两种算法的Perl实现。
编辑添加:
根据讨论,我仍然认为最长公共子串可能是这个问题的核心。即使在您在评论中引用的Journal示例中,该集合的定义特征也是子字符串“Journal”。
我首先考虑什么定义一个集合与其他集合分开。这为您提供了划分数据的分区,然后问题在于测量集合中存在多少共性。如果定义特征是公共子字符串,则最长公共子字符串将是逻辑起始点。
为了使集合检测过程自动化,通常需要成对的共性度量,您可以使用它来衡量所有可能对之间的“差异”。然后,您需要一种算法来计算导致总体差异最小的分区。如果差异度量不是最长公共子串,那很好,但是你需要确定它将是什么。显然,它需要是一个可以衡量的具体事物。
还要记住,差异测量的属性将依赖于可用于制作分区的算法。例如,假设diff(X,Y)给出了X和Y之间差异的度量。那么如果你的距离测量值是diff(A,C)<= diff(A,B)+ diff,那么它可能会很有用。 (公元前)。显然,diff(A,C)应该与diff(C,A)相同。
考虑到这一点,我也开始怀疑我们是否可以将“差异”设想为任意两个字符串之间的距离,并且通过严格的距离定义,我们可以在输入字符串上尝试某种cluster analysis。只是一个想法。
好问题!解决方案的步骤是:
我在regroup.py中实现了这种方法。这是一个例子:
$ cat | ./regroup.py --cluster-prefix-len=2
EFgreen
EFgrey
EntireS1
EntireS2
J27RedP1
J27GreenP1
J27RedP2
J27GreenP2
JournalP1Black
JournalP1Blue
JournalP1Green
JournalP1Red
JournalP2Black
JournalP2Blue
JournalP2Green
^D
EFgre(en|y)
EntireS[12]
J27(Green|Red)P[12]
JournalP[12](Bl(ack|ue)|(Green|Red))
这样的事可能有用。
在你给出的例子中,根将有两条边:“E”和“J”。然后“J”分支将分成“Jo”和“J2”。
我正在为这个答案社区维基做标记。请随意扩展它,以便我们可以对这个问题有一个合理的答案。
字符串相似性有很多种方法。我建议看看这个开源库,它实现了许多指标,如Levenshtein距离。
您应该能够使用通用后缀树来实现此目的:在后缀树中查找来自多个源字符串的长路径。
尝试“frak”。它从字符串集创建正则表达式。也许对它的一些修改会对你有所帮助。 https://github.com/noprompt/frak
希望能帮助到你。
提出了许多解决方案来解决寻找共同子串的一般情况。但是,这里的问题更加专业。您正在寻找共同的前缀,而不仅仅是子串。这使它更简单一些。可以在http://www.geeksforgeeks.org/longest-common-prefix-set-1-word-by-word-matching/找到找到最长公共前缀的一个很好的解释
所以我提出的“pythonese”伪代码是这样的(参考find_lcp
实现的链接:
def count_groups(items):
sorted_list = sorted(items)
prefix = sorted_list[0]
ctr = 0
groups = {}
saved_common_prefix = ""
for i in range(1, sorted_list):
common_prefix = find_lcp(prefix, sorted_list[i])
if len(common_prefix) > 0: #we are still in the same group of items
ctr += 1
saved_common_prefix = common_prefix
prefix = common_prefix
else: # we must have switched to a new group
groups[saved_common_prefix] = ctr
ctr = 0
saved_common_prefix = ""
prefix = sorted_list[i]
return groups
对于这个特殊的字符串示例,考虑使用简单的字/数字分离,使其非常简单。
非数字序列显然可以以大写字母(整数)开头。在将所有字符串分成序列组之后,类似于
[Entire][S][1]
[Entire][S][2]
[J][27][Red][P][1]
[J][27][Green][P][1]
[J][27][Red][P][2]
....
[Journal][P][1][Blue]
[Journal][P][1][Green]
然后开始按组分组,你很快就可以看到前缀“Entire”对于某些组是常见的,并且所有子组都有S作为headgroup,因此只有那些变量为1,2。对于J27案例,您可以看到J27只是叶子,但它然后以红色和绿色分支。
所以List <Pair <list,string >> -structure(复合模式,如果我没记错)。
import java.util.*;
class StringProblem
{
public List<String> subString(String name)
{
List<String> list=new ArrayList<String>();
for(int i=0;i<=name.length();i++)
{
for(int j=i+1;j<=name.length();j++)
{
String s=name.substring(i,j);
list.add(s);
}
}
return list;
}
public String commonString(List<String> list1,List<String> list2,List<String> list3)
{
list2.retainAll(list1);
list3.retainAll(list2);
Iterator it=list3.iterator();
String s="";
int length=0;
System.out.println(list3); // 1 1 2 3 1 2 1
while(it.hasNext())
{
if((s=it.next().toString()).length()>length)
{
length=s.length();
}
}
return s;
}
public static void main(String args[])
{
Scanner sc=new Scanner(System.in);
System.out.println("Enter the String1:");
String name1=sc.nextLine();
System.out.println("Enter the String2:");
String name2=sc.nextLine();
System.out.println("Enter the String3:");
String name3=sc.nextLine();
// String name1="salman";
// String name2="manmohan";
// String name3="rahman";
StringProblem sp=new StringProblem();
List<String> list1=new ArrayList<String>();
list1=sp.subString(name1);
List<String> list2=new ArrayList<String>();
list2=sp.subString(name2);
List<String> list3=new ArrayList<String>();
list3=sp.subString(name3);
sp.commonString(list1,list2,list3);
System.out.println(" "+sp.commonString(list1,list2,list3));
}
}