找出字符串中第一个字母和最后一个字母是第一次重复且中间没有重复字母的最长子串

介绍

一位朋友昨天问我了道面试编程题,感觉挺好的,在这里分享一下。

题目

请编写一段代码,满足以下要求:
给出任意一个字符串,打印一个最长子字符串及其长度,如果有相同长度的子字符串,都要一起打印出来,该子字符串满足以下条件:
(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;
}

运行结果:

C运行结果

应朋友要求,再来一份 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

总结

面试的编程题一般都不会太难,只要多刷一些题,在一定时间内,一般都可以写出来。主要是代码熟练度的问题。我这个代码就写了好长时间/捂脸

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值