在O(n)时间内以恒定空间查找给定字符串中的非唯一字符,即没有额外的辅助数组

时间:2014-02-17 20:24:03

标签: string algorithm

如果字符串s只包含小写字母(a-z),则查找(即打印)重复的字符。

例如,如果string s =“aabcacdddec”

输出:a c d

存在这个问题的3种方法:

  1. [强力]检查字符串的每个字符(即s [i]与其他所有字符串并打印,如果两者都相同) 时间复杂度:O(n ^ 2) 空间复杂度:O(1)

  2. [排序然后比较相邻元素]排序后(在O(n log(n)时间内),遍历字符串并检查s [i]和s [i + 1]是否相等 时间复杂度:O(n logn)+ O(n)= O(n logn) 空间复杂度:O(1)

  3. [在数组中存储字符数]创建一个大小为26的数组(以跟踪a - z)和每个s [i],存储在index = s [i]的增量值 - 26在数组中。最后遍历数组并打印值大于1的所有元素(即'a'+ i) 时间复杂度:O(n) 空间复杂度:O(1)但我们有一个单独的数组用于存储每个元素的频率。

  4. 是否有O(n)方法不使用任何数组/哈希表/映射(等)?

    提示:使用BIT向量

4 个答案:

答案 0 :(得分:4)

这是element distinctness problem,所以一般来说 - 没有办法在O(n)没有额外空间的情况下解决它。

然而,如果您将字母表视为常量(az字符只是非常常量),您可以在O(1)空格中创建这些字符的位集[它是常量! ]或检查O(n)中的每个字符是否重复多次,它将是O(constant*n),仍然在O(n)

第一个解决方案的伪代码:

bit seen[] = new bit[SIZE_OF_ALPHABET] //contant!
bit printed[] =  new bit[SIZE_OF_ALPHABET] //so is this!
for each i in seen.length: //init:
    seen[i] = 0
    printed[i] = 0
for each character c in string: //traverse the string:
    i = intValue(c)
    //already seen it and didn't print it? print it now!
    if seen[i] == 1 and printed[i] == 0: 
        print c
        printed[i] = 1
    else:
        seen[i] = 1

第二种解决方案的伪代码:

for each character c from a-z: //constant number of repeats is O(1)
   count = 0
   for each character x in the string: //O(n)
        if x==c:
           count += 1
   if count > 1
        print count

答案 1 :(得分:2)

字符集的大小是常量,因此您可以扫描输入26次。您只需要一个计数器来存储您看到与当前迭代对应的字符的次数。在每次迭代结束时,如果您的计数器大于1,则打印该字符。

它在运行时是O(n),在辅助空间中是O(1)。

答案 2 :(得分:2)

Java实现

public static void findDuplicate(String str) {
        int checker = 0;
        char c = 'a';
        for (int i = 0; i < str.length(); ++i) {
            int val = str.charAt(i) - c;
            if ((checker & (1 << val)) > 0) {
                System.out.println((char)(c+val));
            }else{
                       checker |= (1 << val);                        
                    }

        }
    }

使用int作为存储并执行按位运算符以查找重复项。

它在O(n)..解释如下

输入为“abddc”

i == 0

  STEP #1 : val = 98 - 98  (0)  str.charAt(0) is a and conversion char to int is 98 ( ascii of 'a')

  STEP #2 : 1 << val equal to ( 1 << 0 ) equal to 1  finally 1 & 0 is 0 
  STEP #3 :  checker = 0 | ( 1 << 0)  equal to 0 | 1 equal to 1 checker is 1

i == 1

  STEP #1 : val = 99 - 98  (1)  str.charAt(1) is b and conversion char to int is 99 ( ascii of 'b')

  STEP #2 :  1 << val equal to ( 1 << 1 ) equal to 2  finally 1 & 2 is 0 
  STEP #3 :  checker = 2 | ( 1 << 1)  equal to 2 | 1 equal to 2 finally checker is 2

i == 2

  STEP #1 : val = 101 - 98  (3)  str.charAt(2) is d and conversion char to int is 101 ( ascii of 'd')

  STEP #2 :  1 << val equal to ( 1 << 3 ) equal to 8  finally 2 & 8 is 0 
  STEP #3 :  checker = 2 | ( 1 << 3)  equal to 2 | 8 equal to 8 checker is 8

i == 3

  STEP #1 : val = 101 - 98  (3)  str.charAt(3) is d and conversion char to int is 101 ( ascii of 'd')

  STEP #2 :  1 << val equal to ( 1 << 3 ) equal to 8  finally 8 & 8 is 8
     Now print 'd'  since the value > 0

你也可以使用位向量,取决于它可以节省空间的语言。在java中,我更喜欢使用int来处理这个固定的(只有26个)常量情况

答案 3 :(得分:0)

在C#中实现(递归解决方案)

 static void getNonUniqueElements(string s, string nonUnique)
    {
        if (s.Count() > 0)
        {
            char ch = s[0];
            s = s.Substring(1);
            if (s.LastIndexOf(ch) > 0)
            {
                if (nonUnique.LastIndexOf(ch) < 0)
                    nonUnique += ch;

            }
            getNonUniqueElements(s, nonUnique);
        }
        else
        {
            Console.WriteLine(nonUnique);
            return;
        }
    }
    static void Main(string[] args)
    {
        getNonUniqueElements("aabcacdddec", "");
        Console.ReadKey();
    }