查找最长的子串没有重复 - 帮助优化代码[Ruby]

问题描述 投票:0回答:2

所以我一直试图解决一个Leetcode Question,“给定一个字符串,找到最长子字符串的长度而不重复字符。”

例如

Input: "abcabcbb"
Output: 3 
Explanation: The answer is "abc", with the length of 3. 

目前,我通过使用哈希表来确定子串是否是唯一的,我优化了我的算法。但是,我的代码仍在O(n ^ 2)运行时中运行,因此超出了提交期间的时间限制。

我尝试做的是基本上遍历每个可能的子字符串并检查它是否有任何重复的值。我对这里的蛮力方法有效吗?我知道还有其他方法,比如滑动窗口方法,但我试图首先使用暴力方法。

# @param {String} s
# @return {Integer}
def length_of_longest_substring(s)
    max_length = 0
    max_string = ""
    n = s.length
    for i in (0..n-1)
        for j in (i..n-1)
            substring = s[i..j]
            #puts substring
            if unique(substring)
                if substring.length > max_length
                    max_length = substring.length
                    max_string = substring
                end
            end
        end
    end
    return max_length
end

def unique(string)
    hash = Hash.new(false)
    array = string.split('')
    array.each do |char|
        if hash[char] == true
            return false
        else
            hash[char] = true
        end
    end
    return true
end
ruby hash substring
2个回答
1
投票

途径

这是一种使用将字符映射到索引的哈希的方法。对于字符串s,假设子字符串s[j..j+n-1]中的字符是唯一的,因此子字符串是最长唯一子字符串的候选字符串。因此,下一个元素是e = s[j+n]我们希望确定s[j..j+n-1]是否包括e。如果不是,我们可以将e附加到子字符串,使其保持唯一。

如果s[j..j+n-1]包含e,我们确定n(子字符串的大小)是否大于先前已知子字符串的长度,并更新我们的记录(如果是)。为了确定s[j..j+n-1]是否包含e,我们可以对子字符串执行线性搜索,但是维护哈希值c_to_i更快,其键值对是s[i]=>ii = j..j_n-1。也就是说,c_to_i将子字符串中的字符映射到完整字符串s中的索引。这样我们只能评估c_to_i.key?(e)以查看子串是否包含e。如果子串包含e,我们使用c_to_i来确定其在s中的索引并添加一个:j = c_to_i[e] + 1。因此新的子字符串是s[j..j+n-1],其值为j。请注意,在此步骤中可能会跳过s的几个字符。

无论子串是否包含e,我们现在必须将e附加到(可能更新的)子串,以便它变为s[j..j+n]

def longest_no_repeats(str)
  c_to_i = {}
  longest = { length: 0, end: nil }
  str.each_char.with_index do |c,i|
    j = c_to_i[c]
    if j
      longest = { length: c_to_i.size, end: i-1 } if
        c_to_i.size > longest[:length]
      c_to_i.reject! { |_,k| k <= j }
    end
    c_to_i[c] = i
  end
  c_to_i.size > longest[:length] ? { length: c_to_i.size, end: str.size-1 } :
    longest
end

a = ('a'..'z').to_a
  #=> ["a", "b",..., "z"]

str = 60.times.map { a.sample }.join
  #=> "ekgdaxxzlwbxixhlfbpziswcoelplhobivoygmupdaexssbuuawxmhprkfms"

longest = longest_no_repeats(str)
  #=> {:length=>14, :end=>44} 
str[0..longest[:end]]
  #=> "ekgdaxxzlwbxixhlfbpziswcoelplhobivoygmupdaexs" 
str[longest[:end]-longest[:length]+1,longest[:length]]
  #=>                                "bivoygmupdaexs" 

效率

以下是@ mechnicov代码的基准比较:

require 'benchmark/ips'

a = ('a'..'z').to_a
arr = 50.times.map { 1000.times.map { a.sample }.join }

Benchmark.ips do |x|
  x.report("mechnicov") { arr.sum { |s| max_non_repeated(s)[:length]   } }
  x.report("cary")      { arr.sum { |s| longest_no_repeats(s)[:length] } }
  x.compare!
end

显示:

Comparison:
            cary:       35.8 i/s
       mechnicov:        0.0 i/s - 1198.21x  slower

0
投票

从你的link

输入:“pwwkew”

输出:3

说明:答案是“wke”,长度为3。

这意味着你需要第一个非重复的子串。

我建议这里是这样的方法

def max_non_repeated(string)
  max_string = string.
                 each_char.
                 map.with_index { |_, i| string[i..].split('') }.
                 map do |v|
                   ary = []
                   v.each { |l| ary << l if ary.size == ary.uniq.size }
                   ary.uniq.join
                 end.
                 max

  {
    string: max_string,
    length: max_string.length
  }
end

max_non_repeated('pwwkew')[:string] #=> "wke"
max_non_repeated('pwwkew')[:length] #=> 3

在Ruby <2.6中使用[i..-1]而不是[i..]

© www.soinside.com 2019 - 2024. All rights reserved.