介绍
一位朋友昨天问我了道面试编程题,感觉挺好的,在这里分享一下。
题目
请编写一段代码,满足以下要求:
给出任意一个字符串,打印一个最长子字符串及其长度,如果有相同长度的子字符串,都要一起打印出来,该子字符串满足以下条件:
(1)第一个字母和最后一个字母是第一次重复
(2)这个子字符串是满足条件里面的最长的
(3)第一个字母和最后一个字母之间的字母不能重复
例如,给定字符串为 adsasadmasd, 其中满足条件的是 dmasd
分析
拿到题目,首先想到的就是暴力解法,找到所有满足第一个条件的字符串,然后再一个个筛选,最后找到最长的,然而,这样解的话,面试基本上也就凉了,所以立马放弃这个想法。
然后就是想,求最长子串问题,最常用的方法就是动态规划算法。然后就在这道题上试了一下,发现好像真的可以用哎。认真捋了捋思路,就开始写代码了。
说一下动态规划的思路:
首先,要用动规,就要从小规模开始,一点一点扩大规模,直到得到最优解。这里,我设置了一个一维数组,用来存储字符串中,以每一个字符结尾且满足条件的最长子串长度。 这样我们就可以,从第一个字符开始,记录以每一个字符结尾且满足条件的最长子串长度,直到字符串结尾,就可以算出每一个字符为结尾字符的子串最长长度,挑选出最大的,对应的子串就是我们要找的最长子串。
思路是这样的:
计算第 j 个字符对应的值时,检查前一个字符 i 的前 n-1 个字符(n 代表 i 对应的值),直到找到与 j 相等的字符,之间的长度就是 j 对应的值,没有就置 0。由于字符 i 的前 n-1 个字符已经满足所有字符不重复的条件了,所以,计算 j 的时候不需要特别考虑这个条件。(这里的字符 i, j 是代表第i 个字符和第 j 个字符,其中 j = i+1)
最后,遍历输出最长子串,由于可能有多个子串,因此这里选择遍历的方式寻找。
代码
C :(也是好久没写 C/C++ 了,字符串处理都是现查的,惭愧…)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 10000
int main()
{
char str[N];
gets(str);
int n = strlen(str), i, j, m;
int longest[n]; // 存储字符串中,以每个字符结尾的最长子串的长度
int longestLength = 0; // 存储最长子串的长度
int index = 0; // 存储 longest 数组中,最后一个不为 0 的下标
for(i = 0; i < n; i++)
{
longest[i] = 0;
// 更新当前字符结尾的最大子串长度
if(longest[index] == 0)
m = i + 1;
else
m = longest[index] + i - index -1; // 用于控制重复字符
for(j = 1; j < m; j++)
{
if(str[i-j] == str[i])
{
longest[i] = j + 1;
index = i;
break;
}
}
// 更新最大子串的长度
if(longestLength < longest[i])
longestLength = longest[i];
}
// 输出最长子串
printf("最长子串为:\n");
for(i = 0; i < n; i++)
{
// 找到最长子串的末尾子串
if(longest[i] == longestLength)
{
char sub[longestLength+1];
strncpy(sub, str+i-longest[i]+1, longestLength);
sub[longestLength] = '\0';
printf("%s\n", sub);
}
}
printf("最长子串长度为:%d\n", longestLength);
return 0;
}
运行结果:
应朋友要求,再来一份 java 版的
Java:
public class LongestSubstring {
public static void getLongestSubstring(String string) {
int n = string.length();
int[] longest = new int[n]; // 存储字符串中,以每个字符结尾的最长子串的长度
int longestLength = 0; // 存储最长子串的长度
int index = 0; // 存储 longest 数组中,最后一个不为 0 的下标
for(int i = 0; i < n; i++) {
int m;
longest[i] = 0;
// 更新当前字符结尾的最大子串长度
if(longest[index] == 0)
m = i + 1;
else
m = longest[index] + i - index -1; // 用于控制重复字符
for(int j = 1; j < m; j++)
{
if(string.charAt(i-j) == string.charAt(i))
{
longest[i] = j + 1;
index = i;
break;
}
}
// 更新最大子串的长度
if(longestLength < longest[i])
longestLength = longest[i];
}
// 输出最长子串
for(int i = 0; i < n; i++) {
// 找到最长子串的末尾子串
if(longest[i] == longestLength)
{
System.out.println(string.substring(i - longestLength + 1, i+1));
}
}
// 输出最长子串长度
System.out.println(longestLength);
}
public static void main(String[] args) {
String string = "adsasadmasd";
getLongestSubstring(string);
}
}
运行结果:
dmasd
5
总结
面试的编程题一般都不会太难,只要多刷一些题,在一定时间内,一般都可以写出来。主要是代码熟练度的问题。我这个代码就写了好长时间/捂脸