求解O(n)中具有两个唯一字符的子串数

时间:2013-07-15 02:48:45

标签: algorithm

我正在处理一系列子字符串问题:

给出一个字符串:

  1. 查找仅包含两个具有最大长度的唯一字符的子字符串。
  2. 查找包含AT MOST两个唯一字符的所有子字符串的数量。
  3. 查找包含两个唯一字符的所有子字符串的数量。
  4. 似乎问题1和2有O(n)解决方案。但是我想不出问题3的O(n)解决方案。(Here是问题2的解决方案,here是问题1的解决方案。)。

    所以我想知道问题3的O(n)解决方案是否存在?

    为问题3添加样本输入/输出:

    Given: abbac

    Return: 6

    因为有6个子字符串包含两个唯一字符: AB,ABB,ABBA,BBA,BA,AC

3 个答案:

答案 0 :(得分:1)

  

查找包含两个唯一字符的所有子字符串的数量。

修改:我误解了这个问题。此解决方案可找到至少 2个唯一字符

的唯一子字符串
  1. len

    给出长度为len * (len + 1) / 2的给定字词的子字符串数量

    sum = len * (len + 1) / 2

    1. 我们正在寻找长度大于1的子串。上面的公式包括长度为1的子串。我们需要减去这些子串。
  2. 因此现在2个字母子串的总数为len * (len + 1) / 2 - l

    sum = `len * (len + 1) / 2 - l`
    
    1. 找到最长的连续字符。应用步骤12。 从步骤2获得的sum中减去此当前总和。
    2. 示例实现如下。

      public static int allUniq2Substrings(char s[]) {
          int sum = s.length * (s.length + 1) / 2 - s.length;
          int sameRun = 0;
          for (int i = 0, prev = -1; i < s.length; prev = s[i++]) {
              if (s[i] != prev) {
                  sum -= sameRun * (sameRun + 1) / 2 - sameRun;
                  sameRun = 1;
              } else {
                  sameRun++;
              }
          }
      
          return sum - (sameRun * (sameRun + 1) / 2 - sameRun);
      
      }
      

      allUniq2Substrings("aaac".toCharArray());
      3
      
      allUniq2Substrings("aabc".toCharArray());
      5
      
      allUniq2Substrings("aaa".toCharArray());
      0
      
      allUniq2Substrings("abcd".toCharArray());
      6
      

      修改 让我再试一次。我使用上面的 3 不变量。 这是查找包含至少2个唯一字符的所有子字符串的子问题。 我在上面发布了一个方法,它为我提供了任何长度的唯一子串。我将使用它从包含2个唯一字符的集合中生成子字符串。

      我们只需要跟踪设置长度为2的最长的字符串运行,即2个唯一字符的任意排列。这些运行的总和给出了所需子串的总数。

      public static int allUniq2Substrings(char s[]) {
          int sum = s.length * (s.length + 1) / 2 - s.length;
          int sameRun = 0;
          for (int i = 0, prev = -1; i < s.length; prev = s[i++]) {
              if (s[i] != prev) {
                  sum -= sameRun * (sameRun + 1) / 2 - sameRun;
                  sameRun = 1;
              } else {
                  sameRun++;
              }
          }
      
          return sum - (sameRun * (sameRun + 1) / 2 - sameRun);
      
      }
      
      public static int uniq2substring(char s[]) {
          int last = 0, secondLast = 0;
          int sum = 0;
          for (int i = 1; i < s.length; i++) {
              if (s[i] != s[i - 1]) {
                  last = i;
                  break;
              }
          }
      
          boolean OneTwo = false;
          int oneTwoIdx = -1; //alternating pattern
      
          for (int i = last + 1; i < s.length; ++i) {
              if (s[secondLast] != s[i] && s[last] != s[i]) { //detected more than 2 uniq chars
                  sum += allUniq2Substrings(Arrays.copyOfRange(s, secondLast, i));
                  secondLast = last;
                  last = i;
                  if (OneTwo) {
                      secondLast = oneTwoIdx;
                  }
                  OneTwo = false;
              } else if (s[i] != last) { //alternating pattern detected a*b*a
                  OneTwo = true;
                  oneTwoIdx = i;
              }
      
          }
      
          return sum + allUniq2Substrings(Arrays.copyOfRange(s, secondLast, s.length));
      }
      

      uniq2substring("abaac".toCharArray())
      6
      
      
      uniq2substring("aab".toCharArray())
      2
      
      uniq2substring("aabb".toCharArray())
      4
      
      uniq2substring("ab".toCharArray())
      1
      

答案 1 :(得分:0)

  • 按顺序读取字符串的字符。使用每个连续字符集的第一个字符的索引,运行的长度以及字符是否等于遇到的第2个先前字符来填充数组。调用此数组A.跟踪运行总和并将其初始化为零。叫这个x。

例如,abbaabbccd =&gt; [(0,1,),(1,2,),(3,2,T),(5,2,T),(7,2,F),(9,1) ,F)] = A

  • 对于A中的每个索引i,通过向右移动1找到你得到的索引j,然后只要你落在A的'True'元素上就继续向右移动。
例如,如果i = 0,则j = 3,因为(5,2,T)为真,但(7,2,F)为假。

  • 设x = x +(A [i] [1])*(从k = i + 1到j的总和[[]] [<]]
例如,继续i = 0,我们得到1 * 6 = 6.这对应于以'a'开头的所有6'ab ...'字符串。

  • 现在,运行时间是多少?好吧,请注意在第2步中,您通过向右移动一堆真元素找到了j,直到找到了一个假元素。该元素保持固定直到i = j,此时它再次向右移动。我们有两个指数向右单调移动,因此处理A在A中是线性的,在输入中是线性的。

完成此示例:(从x = 0开始)

  • i = 0,j = 3,x + = 6 - > X = 6
  • i = 1,j = 3,x + = 8 - > X = 14
  • i = 2,j = 3,x + = 4 - > X = 18
  • i = 3,j = 4,x + = 4 - > X = 22
  • i = 4,j = 5,x + = 2 - > X = 24

我应该提一下,你不是每次都计算从i + 1到j的总和,每次递增i时你只需减去适当的数量,只有当j增加时才重新计算它。

答案 2 :(得分:0)

我认为你发布了解决问题2的链接

http://coders-stop.blogspot.in/2012/09/directi-online-test-number-of.html

我们是否可以很容易地为第三个问题的解决方案建模。 只需在

下修改驱动程序即可
int numberOfSubstrings ( string A ) {
    int len = A.length();
    int res = 0, j = 1, c = 1, a[2][2];
    a[0][0] = A[0]; a[0][1] = 1; 
    for(int i=0;i<len;i++) {
        >>int start = -1;
        for (;j<len; j++) {

           c = isInArray(a, c, A[j]);
           >> if (c == 2 && start != - 1) start = j;
           if(c == -1) break;  
        }
        >>c = removeFromArray(a,A[i]);
        res = (res + j - start);
    }
    return res;
}

关于推导的完整解释可以在链接本身中找到:)