首页 > 试题广场 >

最长对称子字符串

[编程题]最长对称子字符串
  • 热度指数:5342 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
给定一个字符串(数字或大小写字母), 找出最长的对称的子串(如有多个,输出任意一个)。
例如:
输入:“abbaad”
输出:“abba”

输入描述:
字符串


输出描述:
字符串
示例1

输入

a1223a

输出

22
function reverseStr(str) {
    return str.split("").reverse().join("");
}

function findStr(str) {
    var maxStrings = "";
    if(str.length == 1 || str == reverseStr(str)) {
        return str;
    }
    for(var i = 0; i < str.length; i++) {
        for(j = str.length; j > i; j--) {
            var subStrings = str.substring(i, j);
            if(subStrings == reverseStr(subStrings)) {
                if(subStrings.length > maxStrings.length) {
                    maxStrings = subStrings;
                }
            }
        }
    }
    return maxStrings;
}
console.log(findStr(readline()))

使用js实现的,其中readline()可以读取输入的字符串,然后每次不断获取子字符串,如果字符串和它的反转字符串一样,并且比当前所得到的最大对称字符串一样,则进行保存
发表于 2019-02-23 21:03:28 回复(3)
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {

        //1、输入字符串
        String str;
        String maxStr="";//最终的结果
        Scanner scanner = new Scanner(System.in);
        str = scanner.nextLine();

        if(str==null)
            return;
        if(str.length()<=1)//字符串长度为1
            maxStr = str;
        if(str.length()==2){ //字符串长度为2
            if(str.charAt(0)==str.charAt(1))
                maxStr = str;
            else
                maxStr = str.substring(1);
        }else{//字符串长度大于等于3时

            //遍历数组,以每个字符为中心找对称字符串
            for(int i=1;i<str.length()-1;i++){
                String strTemp;
                int left=i,right=i;
                if(str.charAt(i-1)==str.charAt(i+1)){
                    left = i-1;
                    right = i+1;

                }else if(str.charAt(i-1)==str.charAt(i)){
                    left = i-1;
                    right = i;
                }else if(str.charAt(i)==str.charAt(i+1)){
                    left = i;
                    right = i+1;
                }
                strTemp = getStr(left, right, str);
                if(strTemp==null){
                    strTemp = str.substring(left,right+1);
                }
                
                if(strTemp.length()>maxStr.length()){
                    maxStr = strTemp;
                }

            }
            System.out.println(maxStr);
        }
    }
    public static String getStr(int left,int right,String s){
        int i =left,j = right;
        while(i>=0&&j<s.length()){
            if(s.charAt(i)==s.charAt(j)){
                i--;
                j++;
            }else{
                return s.substring(i+1, j);
            }
        }
        if(i<0){
            return s.substring(0,j);
        }else if(j>=s.length()){
            return s.substring(i+1,j);
        }
        return null;
    }

}

发表于 2019-02-24 16:24:34 回复(0)
givenString =input()
def isReverse(s):
    return s ==''.join(reversed(s))
answer =""
for i in range(len(givenString)+1):
    for j in range(i):
        target = givenString[j:i]
        if isReverse(target) and len(target) > len(answer):
            answer =target
 
print(answer)
直接暴力过,注意时间复杂度是立方
编辑于 2019-02-23 14:48:19 回复(6)
编写函数,实现对字符串是否是回文的判断;然后枚举即可。
AC代码:
#include<bits/stdc++.h>
using namespace std;
bool isco(string str,int l,int r){ //判断在str中,下标区间[l,r]的字符串是否是回文
    string a1="",a2="";
    for(int i = l;i <= r;i++) a1 += str[i];
    for(int i = r;i >= l;i--) a2 += str[i];
    if(a1==a2) return true;
    return false;
}
string maxs;
int maxn;
int main(){
    string str;
    cin >> str;
    //枚举答案
    for(int l = 0;l < str.size();l++){
        for(int r = l;r < str.size();r++){
            if(isco(str,l,r)){
                if(r-l+1>maxn){
                    maxn = r-l+1;
                    maxs = "";
                    for(int i = l;i <= r;i++) maxs += str[i];
                }
            }
        }
    }
    cout << maxs;
    return 0;
}


发表于 2020-07-25 20:45:12 回复(0)

简单易懂的动态规划法

思路:

        要找出最长的对称子串,也就是最长回文串。想一下回文串的特点,即 从前往后 和 从后往前 的顺序是一样的,但是其他的干扰字符串可就没这个特点了。所以就想出了一种办法:将原字符串s1逆序得到新的字符串s2,然后找出两个字符串中相同的部分,这个相同的部分就是回文串,然后再考虑一下找出最长的回文串酒好了,题目就转变为求两个字符串的最长公共子串问题。

动态规划解法:

dp含义:
        两个字符串对比的情况,一般最简单的dp就是二维的,因此dp[i][j]的含义可以定义为字符串s1的前i个字符和s2前j个字符中,以第i个字符和第j个字符结尾的最长的相同序列长度。
状态转移方程:
        对于dp[i][j]来说,如果当前s1中的第i个字符和s2中的第j个字符相同,那么需要考虑s1的前i-1个字符和s2的前j-1个字符,即dp[i][j] = dp[i-1][j-1] + 1;如果不相同,那么就是0。
初始值:
        因为涉及到i-1操作,为了防止内存溢出,将dp数组的维度在字符串长度的基础上加1。且元素都初始化为0。

代码:

#include<iostream>
#include<vector>
#include<algorithm>
#include<string>

using namespace std;

string maxHui(string s)
{
    int len = s.length();
    if(len <= 1)
        return s;
    string s2 = s;
    reverse(s2.begin(), s2.end());
    vector<vector<int>> dp(len+1, vector<int>(len+1, 0));
    int res = 0, pos = 0;
    for(int i = 1; i <= len; i++)
    {
        for(int j = 1; j < len+1; j++)
        {
            if(s[i-1] == s2[j-1])
                dp[i][j] = dp[i-1][j-1] + 1;
            if(res <= dp[i][j])
            {
                res=dp[i][j];
                pos = i-1;
            }
        }
    }
    return s.substr(pos-res+1, res);
}

int main()
{
    string str;
    while(getline(cin, str))
        cout << maxHui(str) << endl;

    return 0;
}


编辑于 2020-07-23 00:36:46 回复(0)
这是用c++写的马拉车算法,没接触过可能不太容易懂,个人也研究了挺长时间,可以参考一下。
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdio>
using namespace std;

string mk_str(string s) {
    string temp = "$#";
    for (size_t i = 0; i < s.size(); i++) {
        temp = temp + s[i];
        temp = temp + "#";
    }
    return temp;
}

int main() {
    string str;
    cin >> str;
    str = mk_str(str);
    vector<int> len(str.size());
    int right = 0, max_len = 0, max_point = 0, pos = 0;
    for (size_t k = 1; k < str.size(); k++) {
        int i = k;
        len[i] = i < right ? min(right - i, len[2 * pos - i]) : 1;
        while(str[i + len[i]] == str[i - len[i]]) len[i]++;
        if (i + len[i] > right) {
            right = i + len[i];
            pos = i;
        }
        if (len[i] > max_len) {
            max_len = len[i];
            max_point = i;
        }
    }
    for (int i = max_point - max_len + 1; i <= max_point + max_len - 1; i++) str[i] == '#' || cout << str[i];
    cout << endl;
    return 0;
}

发表于 2020-02-17 13:21:54 回复(0)
#include <bits/stdc++.h>
using namespace std;

int main(){
    string s,t="";
    cin>>s;
    int n=s.length(), Max=0;
    for(int i=0;i<n-1;i++){
        int l=i,r=i+1;
        while(l>=0 && r<=n-1 && s[l--]==s[r++])
        if(r-l-1>Max){
            Max = r-l-1;
            t = s.substr(l+1, r-l-1);
        }
    }
    for(int i=1;i<n-1;i++){
        int l=i-1,r=i+1;
        while(l>=0 && r<=n-1 && s[l--]==s[r++])
        if(r-l-1>Max){
            Max = r-l-1;
            t = s.substr(l+1, r-l-1);
        }
    }
    if(t=="")
        cout<<s[0]<<endl;
    else
        cout<<t<<endl;
    return 0;
}

发表于 2019-11-14 00:59:28 回复(0)
#include<iostream>
#include<vector>
#include<string>
using namespace std;

int main()
{
	string str;
	cin >> str;
	int n = str.size();
	pair<int, int> res = { 1, 0 };
	vector<vector<bool>> a(n , vector<bool>(n,false));
	for (int i = n - 1; i >= 0; i--) a[i][i] = true;
	for (int i = n - 1; i >= 0; i--)
	for (int j = i + 1; j < n; j++)
	{
		if (j == i + 1 && str[i] == str[j])
		{
			a[i][j] = true;
			if (j - i + 1 > res.first)
				res = { j - i + 1, i };
		}
		else if (j>i + 1 && str[i] == str[j] && a[i + 1][j - 1])
		{
			a[i][j] = true;
			if (j - i + 1 > res.first)
				res = { j - i + 1, i };
		}
	}
	string str_res = str.substr(res.second, res.first);
	cout << str_res << endl;
	return 0;
}


发表于 2019-08-06 17:25:16 回复(0)
/*
求最长对称子串
考虑动态规划,设dp[x][y]为从x到y的子串是否对称,若对称表示长度,不对称为-1
子串长度从1到n遍历
当 dp[x+1][y-1] != -1 并且 s[x] == s[y] ,则 dp[x][y] = dp[x+1][y-1] + 2 ;
否则  dp[x][y] = -1
边界条件 x==y dp[x][y] = 1 ; x<y dp[x][y] = 0
*/
#include<bits/stdc++.h>
using namespace std;
#define N 10000

int main()
{
//    freopen("input.txt", "r", stdin);
    char s[N];
    cin >> s;
    int n = strlen(s);
    int dp[n][n];
    memset(dp, 0, sizeof(dp));
    for(int i = 0; i < n; i++) {
        for(int j = 0; j + i < n; j++) {
            if(i == 0) dp[j][j] = 1;
            else {
                if(dp[j + 1][j + i - 1] == -1 || s[j] != s[j + i]) dp[j][j + i] = -1;
                else dp[j][j + i] = dp[j + 1][j + i - 1] + 2;
            }
        }
    }
    int ans = 0, x, y;
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
            if(dp[i][j] > ans) {
                ans = dp[i][j];
                x = i;
                y = j;
            }
    for(int i = x; i <= y; i++) {
        cout << s[i];
    }
    cout << endl;
    return 0;
}

发表于 2019-07-13 10:42:07 回复(0)

因为给定一个字符串,只含数字或大小写字母,可以在每相邻字符之间添加'*',就不用分aba和abba两种情况了。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String str = scanner.next();
        // 在每相邻两个字符之间添加‘*’
        str = str.replaceAll(".", "*$0").substring(1);
        int maxArea = 0, maxIndex = 0;
        for (int i = 0; i < str.length(); i++) {
            int temp = getArea(str, i);
            if (temp > maxArea) {
                maxArea = temp;
                maxIndex = i;
            }
        }
        System.out.println(str.substring(maxIndex - maxArea, maxIndex + maxArea + 1).replace("*", ""));
    }

    private static int getArea(String str, int index) {
        int area = 0;
        int left = index - 1, right = index + 1;
        while (left >= 0 && right <= str.length() - 1) {
            // 向左右两边扩展
            if (str.charAt(left--) == str.charAt(right++)) {
                area++;
            } else {
                break;
            }
        }
        return area;
    }
}
编辑于 2019-07-08 11:07:23 回复(0)

有些人没仔细看,晚、安的方法并没有问题。但是它的方法遍历了所有的子字符串,这个工作量是最大的,字符越多压力增加得越快,代码还可以优化,其实可以及时的break出来的。
我的思路是,回文无非两种可能,偶数类对称和奇数类对称,那我从最小的回文开始往外扩大,就没有漏洞了。

排除极端情况单个字母,余下的情况就是一个for循环从左往右遍历,回文的起点不是aa就是aba,所以我先找起点,之后往外圈一次次判断就完了,一旦不符合条件跳出循环对比长度选择保存,然后下一个循环即可,所以就是一个for套一个while。代码是边提交边修补写的,很烂,主要是为了过关答题。

let line = readline();
let max = 0;
let childLine;
 
if(line.length === 1) {
    childLine = line;
}else{
    for(let i = 0;i < line.length; i++) {
        let count = 0;
        while((count < i || count === i) && (line[i-count] === line[i+count+1] || line[i-count-1] === line[i+count+1])) {
            count++;
        }
        if(count > max && line[i-count+1] === line[i+count]) {
            childLine = line.slice(i - count + 1, i + count + 1);
            max = count;
        }elseif(count > max && line[i-count] === line[i+count]) {
            childLine = line.slice(i - count, i + count + 1);
            max = count;
        }
    }
}
print(childLine);

















编辑于 2019-03-10 23:08:27 回复(0)
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
 
int main(){
    string str,tmp,max,rev;
    while(cin >> str){
        max=str[0];
        for(int i=0;i<str.length();i++){
            for(int j=str.length();j>i;j--){
                if(str[i]==str[j]){
                    rev=str.substr(i,j+1-i);
                    tmp= rev;
                    reverse(rev.begin(),rev.end());
                    if(tmp == rev)
                        if(tmp.length()>max.length()){
                            max=tmp;
                            break;
                        }
                }
            }
        }
        cout << max;
    }
    return 0;
}
编辑于 2019-03-09 15:08:52 回复(0)

JavaScript,当时并没有成功,当时忘记reverse()把原数组也会反转,现在测试是可以的,有改进和错误请告诉我,万分感谢

function findLonglestSubStr(str){
    // 将字符串转换为数组
    let arr=str.split('');
    let result=[];

    // 对长度为0和1的字符串直接返回
    if(arr.length<2) return arr.join('');

    for(let i=0;i<arr.length;i++){
        // 从arr截取下标i到结尾的数组给arr2
        let arr2=arr.slice(i);

        // 找到以该字母为起始点的最长回文串
        while(arr2.join('')!==[...arr2].reverse().join('')){
            arr2.pop();
        }
        
        /* 
        如果最终这个数组剩下超过1的长度,而且比最终要返回的结果数组长度要大,则返回结果数组指向这个回文数组
        */
        if(arr2.length!==1 && arr2.length>result.length) result=arr2; 
    }
    return result.join('');
}

console.log(findLonglestSubStr('abbaad')); // abbaa
console.log(findLonglestSubStr('a')); // a
console.log(findLonglestSubStr('abccbd')); //bccb
发表于 2019-02-27 16:48:22 回复(1)
var line=readline();                        //读取输入字符串
var len=line.length;
function reverseStr(str){                   //反转字符串
    return str.split("").reverse().join("");
}
function getSymmetry(str){                  
    var maxSubString="";
    for(var i=0;i<len;i++){                  //两for循环获得子字符串
        for(var j=len;j>i;j--){              //并判断获取的子字符串是否是对称字符串
            var temp=str.substring(i,j);
            if(temp===reverseStr(temp) && maxSubString.length<temp.length){
                maxSubString=temp;
            }
        }
    }
    return maxSubString;
}
if(len==1){
    console.log(line)
}else{
    console.log(getSymmetry(line));
}

发表于 2019-02-25 19:37:08 回复(0)
这个题目的数据可能很水。。我直接一个双重循环就水过去了。。。我是先写了一个判断是否对称的函数,然后将i固定在字符串的左边,然后将j固定在字符串的右边,一路循环判断是否为对称,如果对称就去比较是否长度大于之前的,大于的话就讲更大的赋值给字符串存起来,具体代码如下:
#include<bits/stdc++.h>
using namespace std;
bool isTrue(string str){
    int len=str.size();
    for(int i=0;i<=len/2;i++){
        if(str[i]!=str[len-i-1]){
            return false;
        }
    }
    return true;
}
int main(){
    string str;
    cin>>str;
    int len=str.size();
    if(len==1){
        cout<<str<<endl;
    }else if(len==2){
        if(str[0]==str[1]){
            cout<<str<<endl;
        }else{
            cout<<str[0]<<endl;
        }
    }else{
        string maxString="";
        int first=0,end=len;
        for(int i=0;i<len;i++){
            for(int j=len;j>i;j--){
                string temp=str.substr(i,j);
                if(isTrue(temp)){
                    if(temp.size()>maxString.size()){
                        maxString=temp;
                    }
                }
            }
        }
        cout<<maxString<<endl;
    }
    return 0;
}

发表于 2019-03-02 21:04:56 回复(0)

这个题是LeetCode的第5题 最长回文子串,算是第二次遇到,但是我写的还是比人家的答案长得多,乱得多。
思路是:先确定中心再向两边延伸,回文串有两种:
①中心的两个字符是一样的,如"abccba";
②中心只有一个字符,如"abcba"。

所以针对两种情况要分别来求:
①针对第一种,每个字符都可以是中心;
②针对第二种,必须先找到"cc",即通过s[i] == s[i - 1]这样的判断找到两个c中的某一个,然后向两边延伸着找。

下面是LeetCode一个写的比较简洁的答案:

import java.util.*;
public class Main {
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        String input = in.nextLine();
        System.out.println(solve(input));
    }

    public String solve(String s) {
        if (s == null || s.length() < 1) return "";
        int start = 0, end = 0;
        for (int i = 0; i < s.length(); i++) {
            int len1 = expandAroundCenter(s, i, i);
            int len2 = expandAroundCenter(s, i, i + 1);
            int len = Math.max(len1, len2);
            if (len > end - start) {
                start = i - (len - 1) / 2;
                end = i + len / 2;
            }
        }
        return s.substring(start, end + 1);
    }

    private int expandAroundCenter(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            left--;
            right++;
        }
        return right - left - 1;
    }
}
编辑于 2019-03-06 16:04:16 回复(5)
//动态规划,转移方程:
//dp[i][j]=dp[i+1][j-1]&&str[i]==str[j] (i+1<=j-1)
//dp[i][j]=str[i]==str[j]
#include <bits/stdc++.h>
using namespace std;
int main(){
    string str;
    int x=0,y=0;
    cin>>str;
    vector<vector<bool>> dp(str.size(),vector<bool>(str.size(),false));
    for(int i=0;i<str.size();i++)
        dp[i][i]=true;
    for(int k=1;k<str.size();k++){
        int j=k,i=0;
        while(j<str.size()&&i<str.size()){
            if(i+1<=j-1)
                dp[i][j]=dp[i+1][j-1]&&str[i]==str[j];
            else
                dp[i][j]=str[i]==str[j];
            if(dp[i][j]==true){
                x=i;
                y=j;
            }
            i++;
            j++;
        }
    }
    cout<<str.substr(x,y-x+1);
    return 0;
}

发表于 2019-11-16 21:25:45 回复(0)

所有的答案里面居然没有用O(n)复杂度的manacher算法。。。都是O(n^2)的暴力解法。。下面我提供一个AC了的manacher方法,供大家参考,如果是笔试O(n^2)的复杂度能AC那也ok,如果是面试,让你提供一个O(n)复杂度的找最长回文子串的算法,希望能想到是用manacher算法

import java.util.Scanner;
import static java.lang.System.in;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(in);
        String str = sc.nextLine();
        System.out.println(manacherProcess(str));
    }

    public static char[] manacherStr(String str) {
        char[] arr = str.toCharArray();
        char[] manArr = new char[arr.length * 2 + 1];
        for (int i = 0, j = 0; i < manArr.length; i++) {
            manArr[i] = (i & 1) == 0 ? '#' : arr[j++];
        }
        return manArr;
    }

    public static String manacherProcess(String str) {
        char[] manArr = manacherStr(str);
        int[] pArr = new int[manArr.length];
        int index = -1;
        int pR = -1;
        int maxVal = Integer.MIN_VALUE;
        int maxIndex = -1;
        for (int i = 0; i < pArr.length; i++) {
            pArr[i] = pR > i ? Math.min(pArr[2 * index - i], pR - i) : 1;
            while (i + pArr[i] < manArr.length && i - pArr[i] > -1) {
                if (manArr[i + pArr[i]] == manArr[i - pArr[i]]) {
                    pArr[i]++;
                } else {
                    break;
                }
            }
            if (i + pArr[i] > pR) {
                pR = i + pArr[i];
                index = i;
            }
            if (pArr[i] > maxVal) {
                maxVal = pArr[i];
                maxIndex = i;
            }
        }
        String ret = "";
        for (int i = maxIndex - maxVal + 1; i < maxIndex + maxVal; i++) {
            ret += (i & 1) == 0 ? "" : manArr[i];
        }
        return ret;
    }
}
发表于 2019-08-08 12:35:21 回复(2)
const str1=readline()
let arr =str1.split("")
let str=""
let tem=[]
tem.push(arr[0])
var iss=(str)=>{
    let flag=true
    for(let i=0;i<Math.ceil((str.length)/2);i++){
        if(str[i]!==str[[str.length-1]-i]) flag= false
    }
    return flag
}
for(let i=0;i<arr.length;i++){
    for(let j=i+1;j<arr.length;j++){
        if(iss(str1.substring(i,j+1))){
            tem.push(str1.substring(i,j+1))
        }
    }
}
let max=0
let s=""
tem.forEach((i,index)=>{
    let curr=i.length
    if(curr>=max){
        max=curr
        s=i
    }
})

console.log(s)
发表于 2022-06-24 17:30:32 回复(0)
import java.util.*;
public class Main {
    public static void main(String[] args){
        Scanner sc= new Scanner(System.in);
        String input = sc.nextLine();
        System.out.println(func(input));
    }
     public static String func(String s) {
         int begin=0;
         int end=0;
         for(int i=0;i<s.length();i++){
             int[] l1=a(s,i,i);
             int[] l2=a(s,i,i+1);
             if(l1[1]-l1[0]>end-begin)
                 {end=l1[1];
                  begin=l1[0];
             }
             if(l2[1]-l2[0]>end-begin)
                   {end=l2[1];
                  begin=l2[0];
             }
         }
         return s.substring(begin,end+1);
     }
    public static int[] a(String s ,int begin,int end){
        while(begin>=0&&end<s.length()&&s.charAt(begin)==s.charAt(end)){
            begin--;
            end++;
        }
        //找到回文,返回最后一次迭代前的状态
        return new int[]{begin+1,end-1};
    }
}

发表于 2020-07-18 21:46:27 回复(0)