题目:
求一个字符串中不重复字符的最长子串,如字符串"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的过程:
/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());
}
代码中的判断我做了些调整,和上面写的不完全一样。
最后,免责说明:
本人对文章的准确性专业性权威性不负任何责任,望各位睁大眼睛自己甄别,如有错误或更好的见解请在评论中指出,谢谢!