如何在不重复字符的情况下找到最长的子字符串?

时间:2013-06-13 11:23:54

标签: string algorithm language-agnostic

我想要一个算法来查找给定字符串中不包含重复字符的字符的最长子字符串。我可以想到一个O(n * n)算法,它考虑给定字符串的所有子字符串并计算非重复字符的数量。例如,考虑字符串“ AABGAKG ”,其中唯一字符的最长子字符串长度为5个字符,对应于 BGAKG

有人能建议更好的方法吗?

由于

编辑:我想我无法向别人正确解释我的问题。你可以在子字符串中有重复的字符(并不是我们需要geeksforgeeks解决方案所做的子字符串中的所有不同字符)。我必须找到的是在任何子字符串中最多没有非重复字符(可能是某些字符重复的情况)。

例如,比如字符串是 AABGAKGIMN ,那么 BGAKGIMN 就是解决方案。

8 个答案:

答案 0 :(得分:3)

对于每个开始 = 0 ...(n-1),尝试将 end 扩展到最右边的位置。

保持使用bool数组[26]以记住是否已使用任何字符。 假设我们目前已完成(开始,结束

开始+ 1 ,

  • 首先通过set清除:used [str [start]] = false;
  • while((end + 1< n)&&(!used [str [end + 1]])){used [str [end + 1]] = true; ++端;}

现在我们检查新的(开始,结束)。总复杂度为O(N)。

答案 1 :(得分:1)

这个怎么样:

public static String getLongestSubstringNoRepeats( String string ){
    int iLongestSoFar = 0;
    int posLongestSoFar = 0;
    char charPrevious = 0;
    int xCharacter = 0;
    int iCurrentLength = 0;
    while( xCharacter < string.length() ){
        char charCurrent = string.charAt( xCharacter );
        iCurrentLength++;
        if( charCurrent == charPrevious ){
            if( iCurrentLength > iLongestSoFar ){
                iLongestSoFar = iCurrentLength;
                posLongestSoFar = xCharacter;
            }
            iCurrentLength = 1;
        }
        charPrevious = charCurrent;
        xCharacter++;
    }
    if( iCurrentLength > iLongestSoFar ){
        return string.substring( posLongestSoFar );
    } else {
        return string.substring( posLongestSoFar, posLongestSoFar + iLongestSoFar );
    }
}

答案 2 :(得分:1)

这是C#中的解决方案。我在Visual Studio 2012中测试了它的工作原理

public static int LongestSubstNonrepChar(string str) {

        int curSize = 0;
        int maxSize = 0;
        int end = 0;
        bool[] present = new bool[256];


        for (int start = 0; start < str.Length; start++) {
            end = start;
            while (end < str.Length) {
                if (!present[str[end]] && end < str.Length)
                {
                    curSize++;
                    present[str[end]] = true;
                    end++;
                }
                else
                    break;
            }
            if (curSize > maxSize) {
                maxSize = curSize;
            }
            //reset current size and the set all letter to false
            curSize = 0;
            for (int i = 0; i < present.Length; i++)
                present[i] = false;
        }

            return maxSize;
    }

答案 3 :(得分:1)

  

非常棘手的问题,我给你一个基于C#的O(n)解决方案。

public string MaxSubStringKUniqueChars(string source,int k) {

if (string.IsNullOrEmpty(source) || k > source.Length) return string.Empty;

        var start = 0;
        var ret = string.Empty;
        IDictionary<char, int> dict = new Dictionary<char, int>();

        for (var i = 0; i < source.Length; i++)
        {
            if (dict.ContainsKey(source[i]))
            {
                dict[source[i]] = 1 + dict[source[i]];
            }
            else
            {
                dict[source[i]] = 1;
            }

            if (dict.Count == k + 1)
            {
                if (i - start > ret.Length)
                {
                    ret = source.Substring(start, i - start);
                }

                while (dict.Count > k)
                {
                    int count = dict[source[start]];
                    if (count == 1)
                    {
                        dict.Remove(source[start]);
                    }
                    else
                    {
                        dict[source[start]] = dict[source[start]] - 1;
                    }

                    start++;
                }
            }

        }
        //just for edge case like "aabbcceee", should return "cceee"
        if (dict.Count == k && source.Length - start > ret.Length)
        {
            return source.Substring(start, source.Length - start);
        }

        return ret;
    }

`

  

//这是测试用例。

    public void TestMethod1()
    {
        var ret = Item001.MaxSubStringKUniqueChars("aabcd", 2);
        Assert.AreEqual("aab", ret);

        ret = Item001.MaxSubStringKUniqueChars("aabbccddeee", 2);
        Assert.AreEqual("ddeee", ret);

        ret = Item001.MaxSubStringKUniqueChars("abccccccccaaddddeeee", 3);
        Assert.AreEqual("ccccccccaadddd", ret);

        ret = Item001.MaxSubStringKUniqueChars("ababcdcdedddde", 2);
        Assert.AreEqual("dedddde", ret);
    }

答案 4 :(得分:0)

设s为给定字符串,n为其长度。

将f(i)定义为s的最长[连续]子串,以s [i]结尾,带有不同的字母。这是独特的,定义明确的。

为每个i计算f(i)。从f(i-1)和s [i]:

推导出来很容易
  • 如果字母s [i]在f(i-1)中,则令j为最大位置j&lt;我这样s [j] = s [i]。然后f(i)是s [j + 1 .. i](用Python表示法)
  • 否则,f(i)为f(i-1),附加s [i]。

你的问题的解决方案是任何f(i)的最大长度(不一定是唯一的)。

您可以将此算法实现为在O(n * 26)时间内运行,其中26是字母表中的字母数。

答案 5 :(得分:0)

public static int longestNonDupSubstring(char [] str){

    int maxCount = 0;
    int count = 0;
    int maxEnd = 0;

    for(int i=1;i < str.length;i++) {

        if(str[i] != str[i-1]) {
            count++;
        }

        if (str[i] == str[i-1]) {
            if(maxCount<count) {
                maxCount = count;
                maxEnd = i;
            }
            count = 0;
        }

        if ( i!=str.length-1 && str[i] == str[i+1]) {
            if(maxCount<count) {
                maxCount = count - 1;
                maxEnd = i-1;
            }
            count = 0;
        }
    }

    int startPos = maxEnd - maxCount + 1;
    for(int i = 0; i < maxCount; i++) {

        System.out.print(str[startPos+i]);
    }

    return maxCount;
}

答案 6 :(得分:0)

  //Given a string ,find the longest sub-string with all distinct characters in it.If there are multiple such strings,print them all.
  #include<iostream>
  #include<cstring>
  #include<array>

  using namespace std;

 //for a string with all small letters
 //for capital letters use 65 instead of 97

 int main()
 {

  array<int ,26> count ;

  array<string,26>largest;

  for(int i = 0 ;i <26;i++)
  count[i]=0;

  string s = "abcdefghijrrstqrstuvwxyzprr";
  string out = "";

  int k = 0,max=0;

  for(int i = 0 ; i < s.size() ; i++)
     { 
         if(count[s[i] - 97]==1)
           {  
              int loc = out.find(s[i]);

              for(int j=0;j<=loc;j++)   count[out[j] - 97]=0;

              if(out.size() > max) 
                {   
                   max = out.size();
                   k=1;
                   largest[0] = out;
                 }
             else if(out.size()==max) largest[k++]=out;

             out.assign(out,loc+1,out.size()-loc-1);
           }
         out = out + s[i];
         count[s[i] - 97]++;
      }

   for(int i=0;i<k;i++)  cout<<largest[i] << endl;
   //output will be
   // abcdefghijr
   // qrstuvwxyzp

  }

答案 7 :(得分:-1)

string MaximumSubstringNonRepeating(string text)
{
    string max = null;
    bool isCapture = false;
    foreach (string s in Regex.Split(text, @"(.)\1+"))
    {
        if (!isCapture && (max == null || s.Length > max.Length))
        {
            max = s;
        }
        isCapture = !isCapture;
    }
    return max;
}

.匹配任何字符。 ( )捕获该角色。 \1再次匹配捕获的角色。 +重复那个角色。整个模式匹配任何一个字符的两次或多次重复。 "AA"",,,,"

Regex.Split()在模式的每个匹配处拆分字符串,并返回其间的片段数组。 (一个警告:它还包括捕获的子串。在这种情况下,正在重复的一个字符。捕获将显示在各个部分之间。这是我刚添加isCapture标志的方式。)

该功能会删除所有重复的字符,并返回重复每组重复字符之间的最长片段。

>>> MaximumSubstringNonRepeating("AABGAKG") // "AA" is repeated
"BGAKG"

>>> MaximumSubstringNonRepeating("AABGAKGIMNZZZD") // "AA" and "ZZZ" are repeated.
"BGAKGIMN"