字符串中的第一个唯一字符

时间:2016-09-23 02:14:12

标签: python

给定一个字符串,找到其中的第一个非重复字符并返回其索引。如果它不存在,则返回-1。

first_unique('leetcode')  # 0
first_unique('loveleetcode')  # 2

我提出了以下解决方案。如何才能使非常长的输入字符串更有效?

def first_unique(self, s):
    if s == '':
        return -1

    for item in s:
        if s.count(item) == 1:
            return s.index(item)
            break

    return -1

8 个答案:

答案 0 :(得分:4)

我的解决方案使用Counter模块中的collections

from collections import Counter
def first_unique(s):
    c = Counter(s)
    for i in range(len(s)):
        if c[s[i]] == 1:
            return i
    return -1

答案 1 :(得分:4)

Suave版本:

from collections import Counter, OrderedDict

class OrderedCounter(Counter, OrderedDict):
    pass

def first_unique(s):
    counter = OrderedCounter(s)
    try:
        return counter.values().index(1)
    except ValueError:
        return -1

奇怪的版本:

from collections import OrderedDict

def first_unique(s):
    nah = {0}-{0}  # funny pair of eyes 
    yeah = OrderedDict()
    for i,c in enumerate(s):
        if c not in nah:
            try:
                del yeah[c]
            except KeyError:
                yeah[c] = i
            else:
                nah.add(c)
    return next(yeah.itervalues(), -1)

答案 2 :(得分:1)

对于“好”字符串的少数情况,你的版本也不错......但对于长“坏”字符串使用count非常昂贵,我建议你缓存项目,例如:

def f1(s):
    if s == '':
        return -1
    for item in s:
        if s.count(item) == 1:
            return s.index(item)
            break
    return -1


def f2(s):
    cache = set()

    if s == '':
        return -1
    for item in s:
        if item not in cache:
            if s.count(item) == 1:
                return s.index(item)
            else:
                cache.add(item)

    return -1

import timeit
import random
import string

random.seed(1)

K, N = 500, 100000
data = ''.join(random.choice(string.ascii_uppercase + string.digits)
               for _ in range(K))

print(
    timeit.timeit('f1(data)', setup='from __main__ import f1, data', number=N))
print(
    timeit.timeit('f2(data)', setup='from __main__ import f2, data', number=N))

我的笔记本电脑上的结果是:

32.05926330029437
4.267771588590406

使用缓存的版本为您提供8倍的加速因子,而不是您使用计数功能。所以,我的一般建议是......尽可能地缓存是否可能

编辑:

我已经将Patrick Haugh版本添加到基准测试中,它给了10.92784585620725

EDIT2:

我已将Mehmet Furkan Demirel版本添加到基准测试中,它给了10.325440507549331

EDIT3:

我已经在基准测试中添加了wim版本,它给了12.47985351744839

结论:

我使用的是我最初提出的使用简单缓存而不依赖于Python计数器模块的版本,它没有必要(就性能而言)

答案 3 :(得分:0)

我会使用for循环从开头和每个索引迭代php artisan migrate,我会使用Substring检查String的其余部分是否在当前索引处具有该字符。 / p>

试试这个:

String

希望它有所帮助!

答案 4 :(得分:0)

此解决方案的想法是使用一对默认值。第一个包含每个字符的整数计数,第二个包含最新字符读取的索引位置。

在阅读完所有字符后,列表理解用于查找仅发生过一次的所有字符(result)。这些字符的最小索引位置(在我们的其他defaultdict order中找到)将为我们提供非重复字符的第一个索引位置。

from collections import defaultdict
# To Create random string:
from string import ascii_lowercase
from random import choice, randint, seed

# Create a random sentence of 1000 words (1-8 characters each).
seed(0)
gibberish = ' '.join(''.join(choice(ascii_lowercase) 
                             for _ in range(randint(1, 8))) 
                     for _ in range(1000))
print(len(gibberish))
# Output: 5614

# Solution.
def first_unique(s):
    dd = defaultdict(int)
    order = defaultdict(int)
    for n, c in enumerate(s):
        dd[c] += 1
        order[c] = n
    result = [order[c] for c in dd if dd[c] == 1]
    return min(result) if result else -1

计时

%timeit first_unique(gibberish)
100 loops, best of 3: 2.13 ms per loop

@wim solution:
%timeit first_unique(gibberish)
100 loops, best of 3: 5.06 ms per loop

@PatrickHaugh solution (which is much easier to understand than mine):
%timeit first_unique(gibberish)
100 loops, best of 3: 4.2 ms per loop

@BPL solution:
%timeit f1(gibberish)
10 loops, best of 3: 39.2 ms per loop
%timeit f2(gibberish)
1000 loops, best of 3: 890 µs per loop

使用更短的20个单词(133个字符)的句子:

%timeit first_unique(gibberish)
10000 loops, best of 3: 62.8 µs per loop

@wim solution:
%timeit first_unique(gibberish)
10000 loops, best of 3: 169 µs per loop

@PatrickHaugh solution:
%timeit first_unique(gibberish)
10000 loops, best of 3: 101 µs per loop

@BPL solution:
%timeit f1(gibberish)
10000 loops, best of 3: 55.1 µs per loop
%timeit f2(gibberish)
10000 loops, best of 3: 31 µs per loop

测试案例。

s1 = 'leetcode'
s2 = 'loveleetcode'

>>> first_unique(s1)
0

>>> first_unique(s2)
2

答案 5 :(得分:0)

        public class Main{

            public static void main(String[] args) {
                System.out.println("Input String : GirishRathi");
                System.out.println("Output String : " +firstUniqChar("GirishRathi"));
            }

            public static int firstUniqChar(String s) {

                Map<Character , Integer> map = new HashMap<Character , Integer>();

                for(int i = 0 ; i < s.length() ; i++) {
                    char current = s.charAt(i);
                    if(map.containsKey(current)) {
                        map.put(current, -1);
                    }else {
                        map.put(current , i);
                    }
                }

                int min = Integer.MAX_VALUE;
                for(char c : map.keySet()) {
                    if(map.get(c) > -1 && map.get(c) < min) {
                        min = map.get(c);
                    }
                }

                return min == Integer.MAX_VALUE ? -1 : min;
            }
        }

答案 6 :(得分:0)

字符串中的第一个唯一字符具有以下属性。

  1. 字符的开始索引和结束索引应该相同。
  2. 其起始索引应小于其他唯一字符的起始索引。
public int firstUniqChar(String s) {
        int result = s.length();
        for(char c = 'a'; c <= 'z'; c++){
            int i = s.indexOf(c);
            if(i != -1 && i == s.lastIndexOf(c)){
                result = Math.min(result, i);
            }
        }
        return result == s.length() ? -1 : result;
    }

答案 7 :(得分:0)

这种方法是什么?

def first_unique_letter(text):
    for c in range(0, len(text)):
        if text[c] not in text[0:c] and text[c] not in text[c + 1:len(text)]:
            return text[c]
    return "_"

它会在找到第一个唯一字母后中断for循环,因此比等待整个循环结束更好。如果没有唯一字符,则返回“ _”