1、问题
输入一行字符串,找出其中出现的相同且长度最长的字符串,输出它。
2、解析
例如“yyabcdabjcabceg”,输出应该为abc和3。
可以将字符串yyabcdabjcabceg分解成如下子串
yyabcdabjcabceg
yabcdabjcabceg
abcdabjcabceg
.......
eg
g
1)从最大子串开始正序查找,然后逆序查找,如果两次查找位置不一致说明存在重复子串。
2)利用后缀数组求解
3、方法一
从最大子串开始正序查找,然后逆序查找,如果两次查找位置不一致说明存在重复子串。
#include <iostream>
#include<string>
using namespace std;
int main()
{
string str,tep;
cout<<"请输入字符串"<<endl;
cin>>str;
for(int i=str.length()-1;i>1;i--)//这里虽然没有保证i比j大,但是在内层循环的if语句中,有限制j+i<=str.length()
{
for(int j=0;j<str.length();j++)
{
if(j+i<=str.length())//???这里非常不理解???
{
size_t t=0;
size_t num=0;
tep=str.substr(j,i);//从大到小取子串,待寻找子串
//从前后分别查找字符串,正常情况下是刚开始的长串找不到的,到后来的短串前后才会重
t=str.find(tep);//正序查找子串tep,从0开始查找字符串tep,返回tep的位置
num=str.rfind(tep);//逆序查找子串tep,从0开始从后向前查找字符串,返回tep的位置
if(t!=num)//如果两次查找位置不一致说明存在重复子串
{
cout<<tep<<" "<< t+1<<endl;//输出子串及位置
return 0;//因为是从长到短,所以再短的也就不找了
}
}
}
}
return 0;
}
方法2:后缀数组法(好)
- #include <iostream>
- using namespace std;
- #define MAXCHAR 5000 //最长处理5000个字符
- //a存放待处理字符串,*a作为后缀数组,后缀数组a中存放的都是每一个后缀的第一个字符在待处理字符串c中的位置(地址),如a[0] = &c[0], a[1] = &c[1],..., a[n] = &c[n],由于数组a中的指针分别指向字符串中的每个后缀,所以将数组a命名为"后缀数组"
- char c[MAXCHAR], *a[MAXCHAR];
- //返回p,q重复部分长度
- int comlen( char *p, char *q )
- {
- int i = 0;
- while( *p && (*p++ == *q++) )
- ++i;
- return i;
- }
- //比较两个字符串
- int pstrcmp( const void *p1, const void *p2 )
- {
- return strcmp( *(char* const *)p1, *(char* const*)p2 );
- }
- int main(void)
- {
- char ch;
- int n=0;
- int i, temp;
- int maxlen=0, maxi=0;
- printf("Please input your string:\n");
- n = 0;
- //在读取输入时,初始化后缀数组a,这样,每个元素就都指向输入字符串中的相应字符:
- while( (ch=getchar())!='\n' )
- {
- a[n] = &c[n];
- c[n++] = ch;
- }
- c[n]='\0'; // 将数组c中的最后一个元素设为空字符,以终止所有字符串
- //对后缀数组进行快速排序,以将后缀相近的子串集中在一起
- qsort( a, n, sizeof(char*), pstrcmp );
- //使用comlen函数对数组进行扫描比较邻接元素,以找出最长重复的字符串
- for(i = 0 ; i < n-1 ; ++i )
- {
- temp=comlen( a[i], a[i+1] );
- if( temp>maxlen )
- {
- maxlen=temp;
- maxi=i;
- }
- }
- printf("%.*s\n",maxlen, a[maxi]);
- return 0;
- }
输入一行字符串,找出其中出现的相同且长度最长的字符串,输出它及首字符的位置。例如“yyabcdabjcabceg”,输出结果应该为abc和3。
这个题目存在一个歧义,例如如果测试字符串为aaaaaa,我一开始以为结果是长度为3的aaa,原来正确答案是长度为5的aaaaa。即2个等长的子串可以有部分字符重叠。因此,面试前最好问仔细点题目意图,免得自己把自己给坑了。
首先,这题的2个for循环中的那个if语句明显多于,这个if语句可以放到j的for循环中去, 即for(int j=0;j<str.length();j++)改为for(int j=0;j<=str.length()-i;j++)。
再者,书上给的程序有一个小bug,例如输入测试例子abba,没有任何输出结果,理论上应该输出a:1或者b:2。当测试例子为abca,也没有任何输出结果,理论上应该输出a:1。 这是因为寻找相同的子串时,程序忽略了长度为1的子串,当然这也可能是题目本意。如果不忽略长度为1的子串,直接修改i变量的下限,令for中的i>=1即可。
书中程序思想: 按照长度递减去寻找相同的子串,只要找到第一对相同的子串,则立刻退出程序。因为是按照长度递减的顺序去寻找子串,所以必定能找到最长的相同子串。
以abcab为例子分析如下:
首先寻找长度为4的子串,只能是abca和bcab,再查看这两个子串是否有其他相同的子串。有的话直接输出结果并退出程序。
然后寻找长度为3的子串,只能abc,bca,cab。这3个子串都没有其他相同的子串。
最后寻找长度为2的子串,首先是ab,用find函数返回在abcab中正序查找的位置0,用rfind函数返回在abcab中逆序查找的位置3。这两个位置不相等,说明在不同的位置存在相同的子串ab。直接输出ab:1,然后退出程序。
法二:这道题目和前面的面试例题1非常相似,因此两者的思想也相似,法二的代码也是在面试例题1的方法二的代码基础上稍作修改。
相同子串的首字符必定相等,因此依次遍历str的每个字符作为相等子串的首字符,然后在str中搜索与首字符相等的字符,然后从这两个相等的首字符开始,依次比较下一个字符是否相等,相等的话字符长度就会增加,直到下一个字符不等为止或者到了字符串的末尾。然后把这长度最大的值存放在maxlen中,最后返回长度最大的子串和起始字符的下标。
程序源代码如下,fun函数是书上的源代码,封装成函数形式,去掉if语句后j的范围优化为j<=len-i,同时i的下限变成i>=1,其他不变。fun1是法二的版本。
- #include<iostream>
- #include<string>
- using namespace std;
- //书上的源代码,封装成函数形式,j的范围优化了一下,同时i的下限变成i>=1,其他不变
- pair<int,string> fun(const string &str)
- {
- int count=0;
- string substr,tep;
- int i,j,len=str.length();
- for(i=len-1;i>=1;--i){
- for(j=0;j<=len-i;j++){
- size_t t=0;
- size_t num=0;
- tep=str.substr(j,i);
- t=str.find(tep);
- num=str.rfind(tep);
- if(t!=num){
- count=t+1;
- substr=tep;
- return make_pair(count,substr);
- }
- }
- }
- return make_pair(count,substr);
- }
- pair<int,string> fun1(const string& str)
- {
- int index=0;
- int maxlen=0;
- string substr;
- int i=0,j=0;
- int len=str.length();
- int k=i+1;
- int s,lt;
- while(i<len){
- j=str.find(str[i],k); //从(k~len-1)范围内寻找str[i]
- if(j==string::npos )//若找不到,说明(k~len-1)范围内没有str[i]
- {
- i++;
- k=i+1;
- }else{ //若找到,则必有(j>=i+1)
- s=i;
- lt=1;
- while(str[++s]==str[++j] && j<len){}
- lt=s-i;
- if(lt>maxlen)
- {
- maxlen=lt;
- substr=str.substr(i,lt);
- index=i+1;
- }
- k=j;
- }//else
- }//while
- return make_pair(index,substr);
- }
- int main()
- {
- string str;
- pair<int,string> rs;
- while(cin>>str)
- {
- rs=fun(str);
- cout<<rs.second<<":"<<rs.first<<endl;
- rs=fun1(str);
- cout<<rs.second<<":"<<rs.first<<endl;
- /*书上源代码
- int count=0;
- string substr,tep;
- int i,j,len=str.length();
- for(i=len-1;i>1;--i){
- for(j=0;j<len;j++){
- if(j+i<=len){
- size_t t=0;
- size_t num=0;
- tep=str.substr(j,i);
- t=str.find(tep);
- num=str.rfind(tep);
- if(t!=num){
- count=t+1;
- substr=tep;
- cout<<tep<<" : "<<t+1<<endl;
- return 0;
- }
- }
- }
- }*/
- }
- return 0;
- }
注:转载源地址