在字符串中查找重复内容?

时间:2010-11-19 09:18:58

标签: c# algorithm string seo

您如何解决以下问题:

我有一个带文本的半大文件(大约10页),我想在本文中找到重复的内容。更具体地说,给定一个字符串,找到两个相同的最长字符串。

我一直在研究最长的常见子序列:

http://en.wikibooks.org/wiki/Algorithm_implementation/Strings/Longest_common_subsequence

但是这些实现需要两个字符串作为输入。

也许有服务已经这样做了?

4 个答案:

答案 0 :(得分:2)

这是一个简单(但效率低下)的算法:循环所有可能的子串长度,从最大值下降到1.对于每个长度,将该长度的所有子串放入字典中。如果发现重复,请停止。它必须是最大的一个。这是相应的C#代码:

    public static string FindDuplicateSubstring(string s)
    {
        for (int len = s.Length-1; len > 0; len--) {
            var dict = new Dictionary<string, int>();
            for (int i = 0; i <= s.Length - len; i++) {
                string sub = s.Substring(i, len);
                if (dict.ContainsKey(sub)) return sub;
                else dict[sub] = i;
            }
        }
        return null;
    }

例如,当应用于问题的文本时,最长的重复子字符串是“实现”。注意,允许重叠的子串,即输入“bbbb”返回“bbb”。您的问题不清楚是否要排除重叠案例。有关更快的方法,请参阅我的其他答案。

答案 1 :(得分:1)

“最长公共子序列”算法不要求匹配是连续的子串。例如,对于字符串“computer”和“houseboat”,子序列是“out”。这是你想要的吗?

如果您希望匹配为连续的子串,则称为longest repeated substring problem。该链接描述了使用后缀树的线性时间和空间算法。

如果你想要一些简短的东西,这里有一种基于LCS算法的方法,但没有表格。我们的想法是循环所需子字符串与其副本之间的所有可能的整数移位。对于每个班次,通过扫描一次字符串找到最大的连续匹配。如果输入字符串具有长度n,则存在O(n)个可能的移位并且检查每个移位花费O(n)时间,因此总成本是O(n ^ 2),仅具有恒定的空间量。 (比较我的简单字典答案,它需要O(n ^ 3)时间和O(n ^ 2)空格。)如果你不想重叠匹配(即你想要“bbbb”返回“bb”而不是“bbb” ),然后在检查每个班次时,如果最大匹配超过班次,则停止。这是C#代码:

    public static string FindDuplicateSubstring(string s, 
                                                bool allowOverlap = false)
    {
        int matchPos = 0, maxLength = 0;
        for (int shift = 1; shift < s.Length; shift++) {
            int matchCount = 0;
            for (int i = 0; i < s.Length - shift; i++) {
                if (s[i] == s[i+shift]) {
                    matchCount++;
                    if (matchCount > maxLength) {
                        maxLength = matchCount;
                        matchPos = i-matchCount+1;
                    }
                    if (!allowOverlap && (matchCount == shift)) {
                        // we have found the largest allowable match 
                        // for this shift.
                        break;
                    }
                } else matchCount = 0;
            }
        }
        if (maxLength > 0) return s.Substring(matchPos, maxLength);
        else return null;
    }

我已经根据我的字典答案测试了它,它给出了相同的结果。但是对于长度为3000的随机字符串,字典需要15秒,而上述方法需要60ms(并且内存要少得多)。

答案 2 :(得分:0)

试试这个

public static string FindLargestDuplicateString(
        string text)
    {
        var largest = string.Empty;
        for (var i = '!'; i <= '}'; i++)
        {
            var l = FindLargestDuplicateStringImpl(
                text, i.ToString());
            if (l.Length > largest.Length)
                largest = l;
        }
        return largest;
    }

    private static string FindLargestDuplicateStringImpl(
        string text, string currentLargest)
    {
        bool found = false;
        for (var i = '!'; i <= '}'; i++)
        {
            var comp = currentLargest + i;
            var last = text.LastIndexOf(comp);
            var first = text.IndexOf(comp, 0);
            if (first == -1 || last == -1 || first == last) 
                continue;
            currentLargest = comp;
            found = true;
        }
        return !found ? currentLargest : 
            FindLargestDuplicateStringImpl(text, currentLargest);
    }

答案 3 :(得分:0)

你可以做这样的事情

public static ArrayList<String> split(String line){
    line+=" ";
    Pattern pattern = Pattern.compile("\\w*\\s");
    Matcher matcher = pattern.matcher(line);
    ArrayList<String> list = new ArrayList<String>();
    while (matcher.find()){
        list.add(matcher.group());
    }
    return list;
}

请务必删除所有标点符号

public static void duplicatedWords(String s, int n){
    ArrayList<String> splitted = split(s); 
    System.out.println(splitted);
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    PriorityQueue<String> pq = new PriorityQueue<String>(splitted.size(), new myComp());
    for(int i = 0; i<splitted.size(); i++){
        if(map.get(splitted.get(i)) == null){
            map.put(splitted.get(i), 1);
        }
        else if(map.get(splitted.get(i)) == 1) {
            map.put(splitted.get(i), map.get(splitted.get(i))+1);
            pq.add(splitted.get(i));
        }
    }
    int size = pq.size();
    for(int i = 0; i<size; i++){
        if(i <n)
            System.out.println(pq.remove());
        else 
            break;
    }
}

使用这个比较器:

public static class myComp implements Comparator{
    @Override
    public int compare(Object arg0, Object arg1) {
        String s1 = (String)arg0;
        String s2 = (String)arg1;
        return s2.length()-s1.length();
    }

}