字符串相关面试题(整理)

1. 题目:输入一个字符串,要求找出字符串中最大子串的长度(如字符串abcd13agbf,当重复出现某个字符时,算一个子串,比如abcd13abcd13agb都是子串)

 理解:要是每个字符都不一样,则最大子串长度为0

 思想:用空间换取时间,用一个数组lastPos[256]代表着256个可能的字符(ASC2码),而数组的值代表字符串中这个字符的最后出现位置,初始数组元素都为负数,然后每次取字符串中的一个字符,此字符的ASC2码为c,若lastPos[c]<0,则表示此字符第一次出现,置lastPos[c]为此字符在字符串中的位置,若lastPos[c]>=0,则表示此字符已经出现过,则可以计算这个子串的长度,若比之前的最大子串长,则更新最大子串长度。

 分析:时间复杂度为on遍历一次即可

 

int GetMaxSubStr(const unsigned char *str)

{

    int maxLen = 0; 

    if (NULL == str)

       return 0;

    int lastPos[256]; //代表着256个可能的字符(ASC2码),数组元素值代表字符串中字符的最后出现的位置

    for (int i = 0; i < 256; ++i)

        lastPos[i] = -1;

    for (int pos = 0; str[pos] != ''; ++pos)

    {

        unsigned char c = str[pos]; 

        if (lastPos[c] > -1) //此字符已经出现过

        {

            int len = pos - lastPos[c] + 1; //计算子串的长度

            if (len > maxLen) 

               maxLen = len;

        }

        lastPos[c] = pos;

    }

    return maxLen;

}  

  

2. 找出两个字符串中的最长相同子串

算法的思想:利用二维数组建立两个字串之间的“对应矩阵”,然后初始化矩阵(mat[i][j]10),然后根据矩阵的对角线找出连续出现1的最长段,及在串中的起始位置。

如果只有两个字符串,可以先建一个二维数组(实际上用不到二维数组,下面再详细说),如inputreputation,如下:   
      i   n   p   u   t   
  r   0   0   0   0   0   
  e   0   0   0   0   0   
  p   0   0   1   0   0   
  u   0   0   0   2   0   
  t   0   0   0   0   3   
  a   0   0   0   0   0   
  t   0   0   0   0   1   
  i   1   0   0   0   0   
  o   0   0   0   0   0   
  n   0   1   0   0   0   
  其中的数字含义如下,0表示相应行和列的字符不相等,不是零就表示从这个字符开始,前n个字符是相同的,n就是那个数。比如第5列,第5行是3,表示inputreputation都有put这个子串。这些数里边有一个最大的,找出来就是了。

所以可以看出,对于两个字符串的问题,如果你不是想把所有的最长公共子串都找出来的话,可以逐行逐列地计算,只要在计算过程中保留最大值就可以了。   

怎么"逐行逐列"地计算呢?可以先算第一行第一列,然后每一行每一列都通过上一行上一列算出。如果两个字符不相等,就为零。如果相等,就是左上角的数+1。因此算出了下一行和下一列,上一行和上一列就可以不要了。因为我们纵保存最大值,和产生最大值的位置。所谓动态规划,就是你算出下一行下一列,上一行上一列就可以不要了。

动态规划

private void printLCS(StringBuffer sb, int i, int j, String s1, int[][] flag) {

if (i == 0 || j == 0) {

return;

}

if (flag[i][j] == 1) {

printLCS(sb, i - 1, j - 1, s1, flag);

sb.append(s1.charAt(i - 1));

} else if (flag[i][j] == 2) {

printLCS(sb, i - 1, j, s1, flag);

} else {

printLCS(sb, i, j - 1, s1, flag);

}

}

 public String calculateLCS(String s1, String s2) {

 int[][] result = new int[s1.length() + 1][s2.length() + 1];

int[][] flag = new int[s1.length() + 1][s2.length() + 1];

for (int i = 0; i <= s1.length(); i++) {

result[i][0] = 0;

}

for (int i = 0; i <= s2.length(); i++) {

result[0][i] = 0;

}

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

for (int j = 1; j <= s2.length(); j++) {

if (s1.charAt(i - 1) == s2.charAt(j - 1)) {

result[i][j] = result[i - 1][j - 1] + 1;

flag[i][j] = 1;

} else if (result[i - 1][j] >= result[i][j - 1]) {

result[i][j] = result[i - 1][j];

flag[i][j] = 2;

} else {

result[i][j] = result[i][j - 1];

flag[i][j] = 3;

}

}

}

 

StringBuffer sb = new StringBuffer();

printLCS(sb, s1.length(), s2.length(), s1, flag);

String lcs = sb.toString();

return lcs;

 

}

 

public static void main(String[] args) {

String s1 = "ssssssssssssssssssssssssssssssss";

String s2 = "111sssss11ss";

LCS lcs = new LCS();

System.out.print(lcs.calculateLCS(s1, s2));

}

 

}

  

 

3. 在一个字符串中查找可能的最长子串,该子串由相同字符组成

 

#include <stdio.h>
#include <string.h>
void find(char* str, char*& start, int& length)

{

    char* p = str;

    char* q;

    int len = 0;

    length=0;

    while(*p)

    {

        q = p+1;

        while(*q && *q == *p)

            q++;

        len = q - p;

        if(len>length)

        {

            length = len;

            start = p;

        }

        p=q;

    }

}

 

int main()

{

    char str[1024];

    scanf("%s", str);

    char* p;

    int len;

    find(str,p, len);

    

    p[len]=0;

    printf("%s
", p);

    

    return 0;

}

  

4. 给定一个字符串,输出最长的重复子序列

举例:ask not what your country can do for you,but what youcan do for your country

最长的重复子序列:can do for you

思路:使用后缀数组解决

分析:

1、由于要求最长公共子序列,则需要找到字符串的所有子序列,即通过产生字符串的后缀数组实现。

2、由于要求最长的重复子序列,则需要对所有子序列进行排序,这样可以把相同的字符串排在一起。

3比较相邻字符串,找出两个子串中,相同的字符的个数。

注意,对于一个子串,一个与其重复最多的字符串肯定是紧挨着自己的两个字符串。

步骤:

      1、对待处理的字符串产生后缀数组

      2对后缀数组排序

      3、依次检测相邻两个后缀的公共长度

      4取出最大公共长度的前缀

举例:输入字符串 banana

1、字符串产生的后缀数组:
    a[0]:banana
    a[1]:anana
    a[2]:nana
    a[3]:ana
    a[4]:na
    a[5]:a

2、对后缀数组进行快速排序,以将后缀相近的(变位词)子串集中在一起

    a[0]:a
    a[1]:ana
    a[2]:anana
    a[3]:banana
    a[4]:na
    a[5]:nana

之后可以依次检测相邻两个后缀的公共长度并取出最大公共的前缀

代码:

/*给定出一个字符串,输出最长的重复子字符串*/

#include <iostream>

#include <algorithm>

#include <string>

using namespace std;

const int MaxCharNum = 5000000;

 

bool StrCmp(char* str1,char* str2);

void GenSuffixArray(char* str,char* suffixStr[]);

int ComStrLen(char* str1,char* str2);

void GenMaxReStr(char* str); 

 

int main()

{

char str[MaxCharNum];

cin.getline(str,MaxCharNum);//遇到回车结束

GenMaxReStr(str);

system("pause");

return 1;

}

 

void GenMaxReStr(char* str)

{

int len = strlen(str);

int comReStrLen = 0;

int maxLoc = 0;

int maxLen = 0;

char* suffixStr[MaxCharNum];

GenSuffixArray(str,suffixStr);//产生后缀数组

//对后缀数组进行排序

sort(suffixStr,suffixStr+len,StrCmp);

 

//统计相邻单词中相同的字符数,并输出结果

for (int i = 0;i < len-1;i++ )

{

comReStrLen =  ComStrLen(suffixStr[i],suffixStr[i+1]);

if (comReStrLen > maxLen)

{

maxLoc = i;

maxLen = comReStrLen;

}

}

//输出结果

for (int i = 0;i < maxLen;i++)

{

cout<<suffixStr[maxLoc][i];

}

cout<<endl;

}

/*为字符串产生其后缀数组,并存放到数组suffixStr中*/

void GenSuffixArray(char* str,char* suffixStr[])

{

int len = strlen(str);

for (int i = 0;i < len;i++)

{

suffixStr[i] = &str[i];

}

}

/*返回str1和str2的共同前缀的长度*/

int ComStrLen(char* str1,char* str2)

{

int comLen = 0;

while(*str1 && *str2)

{

if (*str1 == *str2)

{

comLen++;

}

str1++;

str2++;

}

return comLen;

}

 

//字符串升序排序

bool StrCmp(char* str1,char* str2)

{

if (strcmp(str1,str2) >=0 )

{

return false;

}

return true;

}

  

计划、执行、每天高效的活着学着
原文地址:https://www.cnblogs.com/huxiaoyun90/p/3315723.html