如何实现“在字符串中查找第一个唯一字符”的强力解决方案

时间:2018-02-02 01:52:47

标签: python algorithm

如下所述: 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解决方案感兴趣,没什么好看的。上述解决方案的想法是启动一个双循环,其中内循环搜索与外循环的char匹配,如果匹配,则打破内循环并继续到外循环上的下一个char。

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

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

4 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

我首先要注意的是,您当前查找唯一字符的算法无法正常工作。那是因为你不能假设索引i中的字符是唯一的,因为没有索引j在字符串后面找到相同的字符。索引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在他的回答中建议的那样,您可以在内部else循环中添加for块,以便在找不到匹配项时返回代码:

    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并稍后编制索引。

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

from collections import Counter

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

答案 3 :(得分:0)

我不确定你的方法是否适用于偶数回文​​,例如"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挑战中所述。

不需要第二个清单。 @ÓscarLópez的答案是简化的答案。