Java 确定字符串具有所有唯一字符,而不使用其他数据结构,也不使用小写字符

Java 确定字符串具有所有唯一字符,而不使用其他数据结构,也不使用小写字符,java,string,algorithm,bit-manipulation,bitvector,Java,String,Algorithm,Bit Manipulation,Bitvector,这是Gayle Laakmann McDowell在文章中提出的问题之一: 实现一个算法以确定字符串是否具有所有唯一字符。如果不能使用其他数据结构怎么办 作者写道: 通过使用位向量,我们可以稍微减少空间使用量。在下面的代码中,我们将假定字符串仅是小写的'a'到'z'。这将允许我们只使用一个int 作者有以下实现: public static boolean isUniqueChars(String str) { int checker = 0; for (int i = 0; i

这是Gayle Laakmann McDowell在文章中提出的问题之一:

实现一个算法以确定字符串是否具有所有唯一字符。如果不能使用其他数据结构怎么办

作者写道:

通过使用位向量,我们可以稍微减少空间使用量。在下面的代码中,我们将假定字符串仅是小写的
'a'
'z'
。这将允许我们只使用一个int

作者有以下实现:

public static boolean isUniqueChars(String str) {
    int checker = 0;
    for (int i = 0; i < str.length(); ++i) {
        int val = str.charAt(i) - 'a';
        if ((checker & (1 << val)) > 0)
            return false;
        checker |= (1 << val);
    }
    return true;
}
public静态布尔值isUniqueChars(String str){
int-checker=0;
对于(int i=0;ichecker |=(1对于asccii字符集,您可以用4个长字符表示256位:基本上是手工编写一个数组

public static boolean isUniqueChars(String str) {
    long checker1 = 0;
    long checker2 = 0;
    long checker3 = 0;
    long checker4 = 0;
    for (int i = 0; i < str.length(); ++i) {
        int val = str.charAt(i);
        int toCheck = val / 64;
        val %= 64;
        switch (toCheck) {
            case 0:
                if ((checker1 & (1L << val)) > 0) {
                    return false;
                }
                checker1 |= (1L << val);
                break;
            case 1:
                if ((checker2 & (1L << val)) > 0) {
                    return false;
                }
                checker2 |= (1L << val);
                break;
            case 2:
                if ((checker3 & (1L << val)) > 0) {
                    return false;
                }
                checker3 |= (1L << val);
                break;
            case 3:
                if ((checker4 & (1L << val)) > 0) {
                    return false;
                }
                checker4 |= (1L << val);
                break;
        }            
    }
    return true;
}
public静态布尔值isUniqueChars(String str){
长checker1=0;
长checker2=0;
长checker3=0;
长checker4=0;
对于(int i=0;ichecker1 |=(1L你只需要一行……实际上比一行少很多:

if (str.matches("((.)(?!.*\\1))*"))
这使用了一个负的前瞻性来断言字符串中的每个字符以后不会重复


这种方法的时间复杂度为O(n^2),因为对于输入中的所有n个字符,后面的所有字符(其中有n个)都会进行相等性比较。

我认为我们需要一个“附加数据结构”的通用和实用定义。直观地说,我们不想将每个标量整数或指针称为“数据结构”,因为这使得任何禁止“附加数据结构”的规定都毫无意义

我建议我们借用big-O表示法的一个概念:“附加数据结构”是随着数据集的大小而增长的结构

在本例中,OP引用的代码似乎具有O(1)的空间要求,因为位向量恰好适合整数类型。但正如OP所暗示的,问题的一般形式实际上是O(N)


一般情况下的一个解决方案示例是使用两个指针和一个嵌套循环来简单地比较每个字符。空间要求是O(1),但时间要求是O(N^2)。

下面的算法如何

步骤:

将字符串转换为小写

循环遍历字符串中的每个字符

设置变量数据=0

计算偏移量=字符串-97中第一个字符的ascii值


使用掩码=1单线解决方案,设置该位置的标志,无任何额外数据结构:

str.chars().distinct().count() == (int)str.length();

我将此答案作为想法或建议发布。我不确定此解决方案的真实性和运行时间复杂性(但我确实认为有效时间复杂性不应超过
O(n)
,但我非常高兴知道是否有人想解释


所以,这个想法是这样的。 我们使用两个指针(我认为它们不属于数据结构的范畴,因为否则事情就太难了)。一个叫做
fast
,另一个叫做
slow
。正如它们的名字一样,快速指针的遍历速度比慢速指针快(一次两个索引)。我们将继续检查这些位置的角色,直到发生以下两种情况之一:

  • 快速和慢速指向相同的索引(它们的字符现在比较没有意义,因为它们将相等)
  • s[slow]==s[fast]
    (现在不同索引中的两个字符相等,我们可以返回false)
  • 现在,我们只需重置快速指针并使其沿字符串遍历(一个接一个,不再那么快了,嗯?),直到它变为慢,检查
    s[fast]==s[slow]
    是否为真,以防它返回false


    <> P> C++中的代码是这样的:

    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
      string s;
      cin >> s;
      int n = s.size();
      int slow = 0, fast = 1;
      bool flag = false;
      //   cout << "Running..." << endl;
      while (slow != fast) {
        if (s[slow] == s[fast]) {
          flag = true;
          break;
        }
        // cout << "slow: " << s[slow] << " fast: " << s[fast] << endl;
        slow = (slow + 1) % n;
        fast = (fast + 2) % n;
      }
      fast = 0;
      while (!flag && fast != slow) {
        if (s[slow] == s[fast]) {
          flag = true;
          break;
        }
        fast = (fast + 1) % n;
      }
      if (flag) {
        cout << "Duplicates present." << endl;
      } else {
        cout << "No duplicates present." << endl;
      }
      return 0;
    }
    
    #包括
    使用名称空间std;
    int main(){
    字符串s;
    cin>>s;
    int n=s.size();
    int慢=0,快=1;
    布尔标志=假;
    //无法导入数学
    def唯一字符(str):
    #假设字符串可以包含字符
    #a-z这将32位设置为0
    棋盘格=0
    对于范围内的i(len(str)):
    bitAtIndex=ord(str[i])-ord('a'))
    #如果该位已设置在
    #检查者,返回False
    如果((位索引)>=0):
    如果((检查者和((10)):
    打印('重复字符:'+str[i])
    返回错误
    #否则,更新并继续
    #在棋盘格中设置该位
    
    棋盘格=棋盘格|(1)您立即需要一种不同的存储机制,因为a-z在
    checker
    中已经占据了32位中的26位。尽管可能不如
    int
    快,但a对于较大的数据来说还是很方便的sets@vandale位集
    可能违反了不能使用其他数据结构的条件作者使用了一个
    int
    作为26位的集合,你可以说
    int
    确实被用作数据结构,因此她违反了自己的条件。@ajb说得好!我想如果不使用某种数据结构,就不可能找到作者提出的问题的解决方案(就像她使用的
    int
    位向量)。这肯定是在使用另一个数据结构。:-@user949300真的吗?定义“数据结构”。单个int变量可以被视为一个数据结构。这里没有可见的变量。首先,有一个明显的字符串。另外,所有的正则表达式(模式、匹配器等)都是泛型的
    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
      string s;
      cin >> s;
      int n = s.size();
      int slow = 0, fast = 1;
      bool flag = false;
      //   cout << "Running..." << endl;
      while (slow != fast) {
        if (s[slow] == s[fast]) {
          flag = true;
          break;
        }
        // cout << "slow: " << s[slow] << " fast: " << s[fast] << endl;
        slow = (slow + 1) % n;
        fast = (fast + 2) % n;
      }
      fast = 0;
      while (!flag && fast != slow) {
        if (s[slow] == s[fast]) {
          flag = true;
          break;
        }
        fast = (fast + 1) % n;
      }
      if (flag) {
        cout << "Duplicates present." << endl;
      } else {
        cout << "No duplicates present." << endl;
      }
      return 0;
    }