【问题标题】:Longest Substring without repeating characters issue with edge case边缘情况下不重复字符的最长子字符串问题
【发布时间】:2017-07-30 18:47:12
【问题描述】:

我试图解决这个问题:最长的子串没有重复字符。问题是,它在几个测试用例中都失败了,我不知道如何解决它。我需要你的帮助,看看我哪里出错了。

问题:

给定一个字符串,求最长子串的长度 重复字符。

例子:

给定“abcabcbb”,答案是“abc”,长度为3。

给定“bbbbb”,答案是“b”,长度为1。

给定“pwwkew”,答案是“wke”,长度为 3。注意 答案必须是子字符串,“pwke”是子序列而不是 子字符串。

这是我的代码:

function longestSubString(arr){
    let localSum=0,globalSum=0;

    let set = new Set();

    for(let i=0; i<arr.length; i++){
        let current = arr[i];

        //if the key is present in the store.
        if(set.has(current)){
            set.clear();
            localSum = 1;
            set.add(current);
        } else {
            localSum +=1;
            set.add(current);
        }

        if(globalSum < localSum){
            globalSum = localSum;
        }

    }

    return globalSum;
}

测试:

let test = "abcabc"; //returns 3 - correct
let test2 = "bbb"; //returns 1 - correct


let test5 = "dvdf"; //returns 2 - INCORRECT! it should return 3 (i.e for vdf) since I'm doing set.clear() I'm not able to store previous elements.

longestSubString(test5); //incorrect

直播:

https://repl.it/Jo5Z/10

【问题讨论】:

  • 满足set.has(current)条件时,不应从0重新开始,应从上一列表的第二项返回。
  • 但是我如何以编程方式做到这一点?对不起,我在这里遗漏了逻辑:(任何帮助将不胜感激。这是否意味着我总是必须将每个元素的索引存储在地图中?

标签: string algorithm


【解决方案1】:

未完全测试!

function longestSubString(arr){
    let localSum=0,globalSum=0;

    let set = new Set();

    for(let i=0; i<arr.length; i++){
        let current = arr[i];

        //if the key is present in the store.
        if(set.has(current)){
          let a = Array.from(set);
          a.splice(0,a.indexOf(current)+1);
            set = new Set(a);
            set.add(current);
            localSum = set.size;
        } else {
            localSum +=1;
            set.add(current);
        }

        if(globalSum < localSum){
            globalSum = localSum;
        }

    }

    return globalSum;
}

这个想法是,当您重复时,您应该从第一个重复字符之后的字符开始,在您的情况下为 dvdf,当您到达第二个 d 时,您应该从 vd 继续,而不是从 @987654325 @!

【讨论】:

    【解决方案2】:

    您必须考虑子字符串可能从字符串中的任何字符开始。仅当您发现重复项时才删除该集合使您只考虑从等于第一个字符的字符开始的子字符串。

    稍微修改你的 O(logn*n^2) 解决方案:

    function longestSubString(arr){
        let globalSum=0;
    
        for(let i=0; i<arr.length; i++){
            let set = new Set();
            let localSum=0;
            for(let j=i; j<arr.lenght; j++){         
    
                let current = arr[j];
    
                //if the key is present in the store.
                if(set.has(current)){
                    break;
                } else {
                    localSum +=1;
                    set.add(current);
                }
            }
            if(globalSum < localSum){
                globalSum = localSum;
            }
        }
    
        return globalSum;
    }
    

    还有一个 O(n + d)(几乎是线性)的解决方案,d 是字母表中的字符数。见http://www.geeksforgeeks.org/length-of-the-longest-substring-without-repeating-characters/

    【讨论】:

      【解决方案3】:

      这里似乎有很多很长的答案。由于两个观察结果,我想到的实现被简化了:

      • 每当遇到重复字符时,都需要在上一次出现的当前字符之后开始下一个子字符串。
      • Set() 在迭代时按插入顺序创建一个数组。

      function longestSubstring(str) {
        let maxLength = 0
        let current = new Set()
      
        for (const character of str) {
          if (current.has(character)) {
            const substr = Array.from(current)
      
            maxLength = Math.max(maxLength, substr.length)
            current = new Set(substr.slice(substr.indexOf(character) + 1))
          }
      
          current.add(character)
        }
      
        return Math.max(maxLength, current.size)
      }
      
      const tests = [
        "abcabc",
        "bbb",
        "pwwkew",
        "geeksforgeeks",
        "dvdf"
      ]
      
      tests.map(longestSubstring).forEach(result => console.log(result))

      一个简单的编辑允许我们保留最大子字符串的第一次出现而不是最大长度。

      function longestSubstring(str) {
        let maxSubstr = []
        let current = new Set()
      
        for (const character of str) {
          if (current.has(character)) {
            const substr = Array.from(current)
      
            maxSubstr = maxSubstr.length < substr.length ? substr: maxSubstr
            current = new Set(substr.slice(substr.indexOf(character) + 1))
          }
      
          current.add(character)
        }
      
        const substr = maxSubstr.length < current.size ? Array.from(current) : maxSubstr
      
        return substr.join('')
      }
      
      const tests = [
        "abcabc",
        "bbb",
        "pwwkew",
        "geeksforgeeks",
        "dvdf"
      ]
      
      tests.map(longestSubstring).forEach(result => console.log(result))

      正如我们所见,最后一个测试产生了vdf,正如预期的那样。

      【讨论】:

        【解决方案4】:

        以下解决方案获取O(n+d) 时间的长度,并打印最长的非重复字符子字符串:

        public void longestNonRepeatingLength(String a){
                a="dvdf";
                int visitedIndex[] = new int[256];
            int curr_len = 0, max_len = 0, prev_ind = 0, start = 0, end = 1;
            for(int i =0;i<256;i++)
                visitedIndex[i] = -1;
            visitedIndex[a.charAt(0)] = 0;
            curr_len++;
            int i = 0;
            for( i=1;i<a.length();i++){
                prev_ind = visitedIndex[a.charAt(i)];
                if(prev_ind == -1 || i > prev_ind + curr_len)
                    curr_len++;
                else{
                    if(curr_len>max_len){
                                        start = prev_ind + 1;
                                        end = i;
                                        max_len = curr_len;
                                    }
        
                    curr_len = i - prev_ind;
        
                }
                visitedIndex[a.charAt(i)] = i;
            }
                    if(curr_len>max_len){
                                        end = i-1;
                                        max_len = curr_len;
                                    }
            for( i = start;i<=end;i++)
                System.out.print(a.charAt(i));
            System.out.println("");
            System.out.println("Length = "+max_len);
        }
        

        【讨论】:

          【解决方案5】:

          由于set 包含以索引i 结尾的字符串的最大非重复字符集,这意味着当您遇到以前见过的字符时,而不是像现在的代码那样从空集重新开始,你应该从你的集合中删除所有字符,直到那个重复的字符。

          比如说你的输入是"abXcXdef"。当遇到第二个"X" 时,您需要从您的集合中删除"a""b",留下一组("c","X") 作为最长的集合。添加所有其他字符(因为没有一个是重复的),然后您最终得到的最大长度为 5。

          这样的事情应该可以工作:

          function longestSubString(arr) {
              let globalSum = 0;
              let set = new Set();
              for (let i=0; i<arr.length; i++) {
                  let current = arr[i];
                  if (set.has(current)) {
                      while (true) {
                          let removeChar = arr[i - set.count];
                          if (removeChar != current)
                              set.remove(removeChar);
                          else
                              break;
                      }
                  } else {
                      set.add(current);
                      if (set.count > globalSum)
                          globalSum = set.count;
                  }
              }
              return globalSum;
          }
          

          由于每个字符最多添加一次,最多删除一次,这是一个 O(N) 算法。

          【讨论】:

            猜你喜欢
            • 2017-06-20
            • 1970-01-01
            • 2019-09-11
            • 2022-08-14
            • 2023-03-31
            • 2020-12-04
            • 1970-01-01
            • 2020-04-27
            相关资源
            最近更新 更多