【问题标题】:Find duplicate content in string?在字符串中查找重复的内容?
【发布时间】:2010-11-19 09:18:57
【问题描述】:

你将如何解决以下问题:

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

我一直在看最长公共子序列:

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

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

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

【问题讨论】:

  • 您是否只需要“全词”搜索?在这种情况下,将文本拆分为单词并使用 List 或 Dictionary 很简单。

标签: c# algorithm string seo


【解决方案1】:

这是一个简单(但效率低下)的算法:循环所有可能的子字符串长度,从最大值到 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;
    }

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

【讨论】:

    【解决方案2】:

    “最长公共子序列”算法不要求匹配是连续的子字符串。例如,对于字符串“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 秒,而上述方法需要 60 毫秒(而且内存要少得多)。

    【讨论】:

      【解决方案3】:

      试试这个

      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);
          }
      

      【讨论】:

        【解决方案4】:

        你可以这样做

        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();
            }
        
        }
        

        【讨论】:

          猜你喜欢
          • 2012-12-04
          • 1970-01-01
          • 2020-02-24
          • 2014-05-09
          • 1970-01-01
          • 1970-01-01
          • 2018-08-27
          • 1970-01-01
          相关资源
          最近更新 更多