【问题标题】:How to implement a brute force solution to "Finding first unique character in a string"如何实现“在字符串中查找第一个唯一字符”的蛮力解决方案
【发布时间】:2018-02-02 01:52:46
【问题描述】:

如此处所述: https://leetcode.com/problems/first-unique-character-in-a-string/description/

我在这里尝试了一个,但无法完成: https://paste.pound-python.org/show/JuPLgdgqceMQYh5kk0Sf/

#Given a string, find the first non-repeating character in it and return it's index. If it doesn't exist, return -1.
#xamples:
#s = "leetcode"
#return 0.

#s = "loveleetcode",
#return 2.
#Note: You may assume the string contain only lowercase letters.

class Solution(object):
    def firstUniqChar(self, s):
        """
        :type s: str
        :rtype: int
        """
        for i in range(len(s)):
            for j in range(i+1,len(s)):
                if s[i] == s[j]:
                    break          
               #But now what. let's say i have complete loop of j where there's no match with i, how do I return i?

我只对蛮力 N^2 解决方案感兴趣,没有什么比这更有趣的了。上述方案的思路是启动一个双循环,其中内循环搜索与外循环的字符匹配,如果匹配,则中断内循环并继续到外循环的下一个字符。

但问题是,当没有匹配项时,我该如何处理,也就是我需要将外部循环的索引作为第一个唯一索引返回时。

无法找到一种优雅的方式来处理它,并且可以像处理单个字符字符串一样处理边缘情况。

【问题讨论】:

    标签: python algorithm


    【解决方案1】:

    遍历每个字符,并检查它是否出现在以下任何字符中。我们需要跟踪我们已经看到的字符,以避免陷入极端情况。试试这个,这是O(n^2) 解决方案:

    def firstUniqChar(s):
        # store already seen chars
        seen = []
        for i, c in enumerate(s):
            # return if char not previously seen and not in rest
            if c not in seen and c not in s[i+1:]:
                return i
            # mark char as seen
            seen.append(c)
        # no unique chars were found
        return -1
    

    为了完整起见,这里有一个O(n) 解决方案:

    def firstUniqChar(s):
        # build frequency table
        freq = {}
        for i, c in enumerate(s):
            if c not in freq:
                # store [frequency, index]
                freq[c] = [1, i]
            else:
                # update frequency
                freq[c][0] += 1
        # find leftmost char with frequency == 1
        # it's more efficient to traverse the freq table
        # instead of the (potentially big) input string
        leftidx = float('+inf')
        for f, i in freq.values():
            if f == 1 and i < leftidx:
                leftidx = i
        # handle edge case: no unique chars were found
        return leftidx if leftidx != float('+inf') else -1
    

    例如:

    firstUniqChar('cc')
    => -1
    firstUniqChar('ccdd')
    => -1
    firstUniqChar('leetcode')
    => 0
    firstUniqChar('loveleetcode')
    => 2
    

    【讨论】:

    • 如果输入的是'cc'怎么办
    • @user1008636 那不就是return None吗?
    • 不,它会去Else。
    • 有几个边缘情况我没有考虑。请检查我更新的、经过测试的答案。
    • 谢谢,O(n) 解决方案非常棒。
    【解决方案2】:

    else 添加到您返回的for 循环中。

    for j ...:
       ...
    else:
      return i
    

    【讨论】:

      【解决方案3】:

      我首先要指出的是,您当前用于查找唯一字符的算法无法正常工作。那是因为你不能仅仅因为没有一个索引j 在字符串后面找到相同的字符就假设索引i 处的字符是唯一的。索引i 处的字符可能是先前字符的重复(当先前的j 等于当前的i 时,您会跳过该字符)。

      您可以通过让j 遍历整个索引范围来修复算法,并添加一个额外的检查以在索引与您的if 相同时忽略匹配:

      for i in range(len(s)):
          for j in range(len(s)):
              if i != j and s[i] == s[j]:
                  break
      

      正如Ignacio Vazquez-Abrams 在他的回答中建议的那样,您可以在内部for 循环中添加一个else 块,以使代码在未找到匹配项时返回:

          else:   # this line should be indented to match the "for j" loop
              return i
      

      如果您使用 Python 中提供的内置函数和类型,还有一些方法可以更简单地解决此问题。

      例如,您可以实现一个O(n^2) 解决方案,仅使用一个显式循环,并使用str.count 替换内部循环:

      def firstUniqChar(s):
          for i, c in enumerate(s):
              if s.count(c) == 1:
                  return i
          return None
      

      我还使用enumerate 一步将字符值和索引放在一起,而不是遍历range 并稍后建立索引。

      还有一种使用collections.Counter 制作O(n) 解决方案的非常简单的方法,它可以在开始检查字符之前一次性完成所有计数,以便尝试找到第一个唯一的字符:

      from collections import Counter
      
      def firstUniqChar(s):
          count = Counter(s)
          for i, c in enumerate(s):
              if count[c] == 1:
                  return i
          return None
      

      【讨论】:

        【解决方案4】:

        我不确定您的方法是否适用于偶数回文​​,例如"redder"(注意第二个d)。试试这个:

        s1 = "leetcode"
        s2 = "loveleetcode"
        s3 = "redder"
        
        
        def unique_index(s):
            ahead, behind = list(s), set()
            for idx, char in enumerate(s):
                ahead = ahead[1:]
                if (char not in ahead) and (char not in behind):
                    return idx
                behind.add(s[idx])
            return -1
        
        
        assert unique_index(s1) == 0
        assert unique_index(s2) == 2
        assert unique_index(s3) == -1
        

        对于每个角色,我们都会向前看和向后看。只有与两个组不相交的字符才会返回索引。随着迭代的进行,观察到的ahead 的列表会缩短,而看到的behind 会扩展。默认为-1,如实际的 leetcode 挑战中所述。

        不需要第二个列表。 @Óscar López 的答案是简化的答案。

        【讨论】:

          猜你喜欢
          • 2021-03-21
          • 2018-01-19
          • 1970-01-01
          • 2021-10-10
          • 1970-01-01
          • 1970-01-01
          • 2019-04-05
          • 2019-03-16
          • 2020-02-14
          相关资源
          最近更新 更多