【程序猿笔试面试解题指南】求字符串中不含重复字符的最长子串

这道题听说曾出现在百度笔试和大众点评网笔试题中。作为一个有理想有抱负的代码搬运员,在花了一个上午的时间浏览各位大神的解法后决定做个汇总。

题目:

求一个字符串中不重复字符的最长子串,如字符串"abacdefgafg",最长的不重复的子串为“acdefg”,长度为6,当有两个长度相同的字符串,输出第一个最长的字符子串。
from 【校园招聘】2013大众点评网软件研发岗笔试题
勘误:最长子串应该是7个,"bacdefg"才是啊,我还以为我写的程序出错了...

---------------------------------------------------------------------------------------------------
解法1:使用后缀树或者后缀数组。

这里有篇博文写的挺好。我就直接贴出地址了。点这里点这里
解法2:使用动态规划的办法。
这里也有一篇不错的博文。再点这里点这里。
但是,本人智商捉鸡,花了好久都没怎么理解其中精髓,还是决定自己按照自己的智力复述一下作者的解法。
首先,我们需要声明:
原数组:Array[n] 
然后,我们定义三个数组:
prefixLen[i]:表示[i...n-1]这个子串中最长的不重复字符子串的长度。
maxlenStart[i]:表示以Array[i]为起点的最长的不重复字符子串的起点(其实恒有maxlenStart[i]=i,但是作者这么写了,我也就声明一下吧)。
maxlenEnd[i]:表示以Array[i]为起点的最长的不重复字符子串的终点。

于是有:
prefixLen[i]>=maxlenEnd[i]-maxlenStart[i]+1
即,[i...n-1]这个子串中最长的不重复字符子串的长度大于或者等于以i为起点的最长的不重复字符子串。
下面是我们DP的过程。
(缩写prefixLen[i]:P[i],maxlenStart[i]:S[i],maxlenEnd[i]:E[i])
首先定义起始边界:
P[n]=0 S[n]=n E[n]=n-1
然后,注意,我们是从n-1到0这种从字符串右到左的顺序去动态规划的。
首先,肯定有:S[i]=i
然后,我们得讨论:
1.E[i+1]-S[i+1]+1=P[i+1](即i是相邻着i+1..n子串的中最长的不重复字符子串的)
1.1:如果Array[i]和S[i+1]...E[i+1]中元素都不相等,则:
P[i]=P[i+1]+1,S[i]=i,E[i]=E[i+1]
1.2:如果Array[i]和S[i+1]...E[i+1]中元素的某一个相等,则:
找到S[i+1]...E[i+1]中和Array[i]相等的元素Array[j]
P[i]=max(P[i+1],j-i),S[i]=i,E[i]=j-1
2.E[i+1]-S[i+1]+1!=P[i+1](即i不相邻i+1..n子串的中最长的不重复字符子串)
2.1:如果Array[i]和S[i+1]...E[i+1]中元素都不相等,则:
P[i]=max(P[i+1],E[i+1]-S[i+1]+2),S[i]=i,E[i]=E[i+1]
2.1:如果Array[i]和S[i+1]...E[i+1]中元素的某一个相等,则:
找到S[i+1]...E[i+1]中和Array[i]相等的元素Array[j]
P[i]=max(P[i+1],j-i),S[i]=i,E[i]=j-1
下面我们用题中的"abacdefgafg"模拟一下DP的过程:
【程序猿笔试面试解题指南】求字符串中不含重复字符的最长子串_第1张图片
/0:P[11]=0  S[11]=11  E[11]=10
g:P[10]=1  S[10]=10  E[10]=10
f:P[9]=2  S[9]=9  E[9]=10
a:P[8]=3  S[8]=8  E[8]=10
g:P[7]=3  S[7]=7 E[7]=9
....


然后我们从0...n-1中找到i使P[0]=E[i]-S[i]+1,输出Array[S[i]...E[i]]即可。
另外,我们可以做些优化:
每次都判断Array[i]和S[i+1]...E[i+1]中元素的某一个是否相等是费时的,我们可以在预处理中找到Array[i]的下一个相等元素的下标存在Index[i]中。然后每次查看Index[i]是不是在S[i+1]...E[i+1]之内就可知道Array[i]和S[i+1]...E[i+1]中元素的某一个是否相等。
方法是利用Hash(假设字符串是Ascii的)。

void BuildIndex(int *index,string &array,int n)
{
    int hash[128];
    int i;
    for(i=0;i<128;i++)
        hash[i]=-1;
    for(i=n;i>0;--i)
    {
        index[i-1]=hash[array[i-1]];
        hash[array[i-1]]=i-1;
    }
}

完整代码:

#include <iostream>
#include <string>
using namespace std;
void BuildIndex(int *index,string &array,int n)
{
    int hash[128];
    int i;
    for(i=0;i<128;i++)
        hash[i]=-1;
    for(i=n;i>0;--i)
    {
        index[i-1]=hash[array[i-1]];
        hash[array[i-1]]=i-1;
    }
}
void longestSubseqWithUniqueChars(string &array,int n)
{
//    cout<<"Size="<<n<<endl;
    int *index=new int[n];
    BuildIndex(index,array,n);
    int i;
    int *P=new int[n+1];
    int *E=new int[n+1];
    P[n]=0;
    E[n]=n-1;
    for(i=n-1;i>=0;--i)
    {
        if((index[i]>=i+1)&&(index[i]<=E[i+1]))
        {
            E[i]=index[i]-1;
            P[i]=P[i+1]>(index[i]-i)?P[i+1]:(index[i]-i);
        }
        else
        {
            if((E[i+1]-i)==P[i+1])
            {
                P[i]=P[i+1]+1;
                E[i]=E[i+1];
            }
            else
            {
                P[i]=P[i+1]>(E[i+1]-i+1)?P[i+1]:(E[i+1]-i+1);
                E[i]=E[i+1];
            }
        }
    }
for(i=0;i<n;i++)
    if(E[i]-i+1==P[0])
        break;
for(int j=i;j<=E[i];j++)
    cout<<array[j];
cout<<endl;

}
int main()
{
    string input;
    cin>>input;
    longestSubseqWithUniqueChars(input,input.size());
}
代码中的判断我做了些调整,和上面写的不完全一样。

最后,免责说明:

本人对文章的准确性专业性权威性不负任何责任,望各位睁大眼睛自己甄别,如有错误或更好的见解请在评论中指出,谢谢!


你可能感兴趣的:(算法,字符串)