8.26华为OD机试真题 新系统【字符串回文判断】

字符串回文判断(Java/Py/C/C++/Js/Go)题解

华为OD机试真题 新系统 华为OD上机考试真题新系统 8月26号 100分题型

题目内容

给定一个仅由小写英文字母组成的字符串 sss,请判断它是否可以在删除一个字符之后,变成一个回文串。

回文串的定义是:从左到右读和从右到左读完全相同。

如果可以,输出一个数组,数组由可删除字符的索引值构成;否则输出一个空数组。

输入描述

输入一个字符串 sss,2≤s.length≤1052 \le s.length \le 10^52≤s.length≤105

输出描述

输出一个数组,内容为:NNN 种删除方法的字符索引值。如输入为:abca,则输出为:[1, 2],代表可以有两种删除方式:[aba, aca]

  • 1:删除索引值为 1 的字符,即删除字符 b,则变为:aca,属于回文
  • 2:删除索引值为 2 的字符,即删除字符 c,则变为:aba,属于回文

样例1

输入

复制代码
abca

输出

复制代码
1,2

说明

删除 b 或删除 c 后,都可以变成回文串。

样例2

输入

复制代码
abcd

输出

复制代码

说明

无论删除哪个字符,都无法变成回文串。

题解

思路:双指针 + 逻辑分析

  1. 分情况讨论:
    • 输入本身不是回文字符串时,找到两侧第一个不相等leftright位置,尝试任一位置字符删除之后,判断是否为回文串。
    • 输入本身为回文字符串,删除一个字符后,要想仍然是回文,删除的位置必须在"中心连续相同字符区间"里。
  2. 简单解释一下为什么回文字符串删除中心连续相同字符区间内元素还是回文串
    1. 回文串为奇数时,字符串关于中心点对称,删除中心点之后肯定还是回文串 => 变为偶数回文串。要想删除位置向两侧扩展,只需要达到和删除中心点相同影响即可。而中心点两侧连续区域如果和中心点值相同,删除效果一致。
    2. 回文串为偶数时,字符串关于两个中心点对称,删除任意中心点还是回文串 => 变为关于任一中心点对称的奇数回文串,要想删除位置向两侧扩展,只需要达到和删除中心点相同影响即可。而中心点两侧连续区域如果和中心点值相同,删除效果一致。
  3. 基于上述分析,首先通过双指针从二侧向中心搜索,找到第一处不相同位置。如果存在不相同位置说明不是回文字符串,尝试删除左侧或右侧字符,判断是否为回文串。如果不存在相同位置,说明本身是回文串。根据2的分析,使用双指针从中心点向两侧拓展,找到中心连续相同字符区间, 这部分区间就是结果。

C++

cpp 复制代码
#include <bits/stdc++.h>
#include <vector>
using namespace std;

bool check(const string& s, int del) {
    int l = 0, r = s.size() - 1;
    while (l < r) {
        if (l == del) {
            l++;
            continue;
        }
        if (r == del) {
            r--;
            continue;
        }

        if (s[l] != s[r]) return false;

        l++;
        r--;
    }

    return true;
}


vector<int> solve(string& s) {
    int n = s.size();
    vector<int> ans;

    int l = 0, r = n - 1;

    // 找第一处不匹配
    while (l < r && s[l] == s[r]) {
        l++;
        r--;
    }

    if (l < r) {
        // 原字符串不是回文,只可能删除 l 或 r
        if (check(s, l)) ans.push_back(l);
        if (check(s, r)) ans.push_back(r);
    } else {
        // 原字符串本身是回文
        // 合法删除位置就是包含中心的连续相同字符区间
        int left, right;

        if (n % 2 == 1) {
            // 奇数长度,中心是 n / 2
            int mid = n / 2;
            left = right = mid;

            while (left > 0 && s[left - 1] == s[mid])
                left--;

            while (right + 1 < n && s[right + 1] == s[mid])
                right++;
        } else {
            // 偶数长度,两个中心字符一定相等
            int mid1 = n / 2 - 1;
            int mid2 = n / 2;

            left = mid1;
            right = mid2;

            while (left > 0 && s[left - 1] == s[mid1])
                left--;

            while (right + 1 < n && s[right + 1] == s[mid1])
                right++;
        }

        for (int i = left; i <= right; i++)
            ans.push_back(i);
    }
    return ans;
}

int main() {
    string s;
    cin >> s;
    vector<int> ans = solve(s);
    for (int i = 0; i < ans.size(); i++) {
        if (i) cout << ",";
        cout << ans[i];
    }
    return 0;
}

Java

java 复制代码
import java.io.*;
import java.util.*;

public class Main {
    static boolean check(String s, int del) {
        int l = 0, r = s.length() - 1;
        while (l < r) {
            if (l == del) {
                l++;
                continue;
            }
            if (r == del) {
                r--;
                continue;
            }

            if (s.charAt(l) != s.charAt(r)) return false;

            l++;
            r--;
        }

        return true;
    }

    static List<Integer> solve(String s) {
        int n = s.length();
        List<Integer> ans = new ArrayList<>();

        int l = 0, r = n - 1;

        // 找第一处不匹配
        while (l < r && s.charAt(l) == s.charAt(r)) {
            l++;
            r--;
        }

        if (l < r) {
            // 原字符串不是回文,只可能删除 l 或 r
            if (check(s, l)) ans.add(l);
            if (check(s, r)) ans.add(r);
        } else {
            // 原字符串本身是回文
            // 合法删除位置就是包含中心的连续相同字符区间
            int left, right;

            if (n % 2 == 1) {
                // 奇数长度,中心是 n / 2
                int mid = n / 2;
                left = right = mid;

                while (left > 0 && s.charAt(left - 1) == s.charAt(mid))
                    left--;

                while (right + 1 < n && s.charAt(right + 1) == s.charAt(mid))
                    right++;
            } else {
                // 偶数长度,两个中心字符一定相等
                int mid1 = n / 2 - 1;
                int mid2 = n / 2;

                left = mid1;
                right = mid2;

                while (left > 0 && s.charAt(left - 1) == s.charAt(mid1))
                    left--;

                while (right + 1 < n && s.charAt(right + 1) == s.charAt(mid1))
                    right++;
            }

            for (int i = left; i <= right; i++)
                ans.add(i);
        }

        return ans;
    }

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s = br.readLine().trim();

        List<Integer> ans = solve(s);

        for (int i = 0; i < ans.size(); i++) {
            if (i > 0) System.out.print(",");
            System.out.print(ans.get(i));
        }
    }
}

Python

python 复制代码
def check(s, delete):
    l, r = 0, len(s) - 1

    while l < r:
        if l == delete:
            l += 1
            continue

        if r == delete:
            r -= 1
            continue

        if s[l] != s[r]:
            return False

        l += 1
        r -= 1

    return True


def solve(s):
    n = len(s)
    ans = []

    l, r = 0, n - 1

    # 找第一处不匹配
    while l < r and s[l] == s[r]:
        l += 1
        r -= 1

    if l < r:
        # 原字符串不是回文,只可能删除 l 或 r
        if check(s, l):
            ans.append(l)
        if check(s, r):
            ans.append(r)
    else:
        # 原字符串本身是回文
        # 合法删除位置就是包含中心的连续相同字符区间

        if n % 2 == 1:
            # 奇数长度,中心是 n / 2
            mid = n // 2
            left = right = mid

            while left > 0 and s[left - 1] == s[mid]:
                left -= 1

            while right + 1 < n and s[right + 1] == s[mid]:
                right += 1

        else:
            # 偶数长度,两个中心字符一定相等
            mid1 = n // 2 - 1
            mid2 = n // 2

            left = mid1
            right = mid2

            while left > 0 and s[left - 1] == s[mid1]:
                left -= 1

            while right + 1 < n and s[right + 1] == s[mid1]:
                right += 1

        for i in range(left, right + 1):
            ans.append(i)

    return ans


s = input().strip()
ans = solve(s)
print(",".join(map(str, ans)), end="")

JavaScript

js 复制代码
const readline = require('readline');

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

rl.on('line', (s) => {
    s = s.trim();

    function check(s, del) {
        let l = 0, r = s.length - 1;

        while (l < r) {
            if (l === del) {
                l++;
                continue;
            }

            if (r === del) {
                r--;
                continue;
            }

            if (s[l] !== s[r]) return false;

            l++;
            r--;
        }

        return true;
    }

    function solve(s) {
        const n = s.length;
        const ans = [];

        let l = 0, r = n - 1;

        // 找第一处不匹配
        while (l < r && s[l] === s[r]) {
            l++;
            r--;
        }

        if (l < r) {
            // 原字符串不是回文,只可能删除 l 或 r
            if (check(s, l)) ans.push(l);
            if (check(s, r)) ans.push(r);
        } else {
            // 原字符串本身是回文
            // 合法删除位置就是包含中心的连续相同字符区间
            let left, right;

            if (n % 2 === 1) {
                // 奇数长度,中心是 n / 2
                const mid = Math.floor(n / 2);
                left = right = mid;

                while (left > 0 && s[left - 1] === s[mid])
                    left--;

                while (right + 1 < n && s[right + 1] === s[mid])
                    right++;
            } else {
                // 偶数长度,两个中心字符一定相等
                const mid1 = Math.floor(n / 2) - 1;
                const mid2 = Math.floor(n / 2);

                left = mid1;
                right = mid2;

                while (left > 0 && s[left - 1] === s[mid1])
                    left--;

                while (right + 1 < n && s[right + 1] === s[mid1])
                    right++;
            }

            for (let i = left; i <= right; i++)
                ans.push(i);
        }

        return ans;
    }

    const ans = solve(s);
    console.log(ans.join(','));

    rl.close();
});

Go

go 复制代码
package main

import (
	"bufio"
	"fmt"
	"os"
)

func check(s string, del int) bool {
	l, r := 0, len(s)-1

	for l < r {
		if l == del {
			l++
			continue
		}

		if r == del {
			r--
			continue
		}

		if s[l] != s[r] {
			return false
		}

		l++
		r--
	}

	return true
}

func solve(s string) []int {
	n := len(s)
	ans := []int{}

	l, r := 0, n-1

	// 找第一处不匹配
	for l < r && s[l] == s[r] {
		l++
		r--
	}

	if l < r {
		// 原字符串不是回文,只可能删除 l 或 r
		if check(s, l) {
			ans = append(ans, l)
		}
		if check(s, r) {
			ans = append(ans, r)
		}
	} else {
		// 原字符串本身是回文
		// 合法删除位置就是包含中心的连续相同字符区间
		var left, right int

		if n%2 == 1 {
			// 奇数长度,中心是 n / 2
			mid := n / 2
			left = mid
			right = mid

			for left > 0 && s[left-1] == s[mid] {
				left--
			}

			for right+1 < n && s[right+1] == s[mid] {
				right++
			}
		} else {
			// 偶数长度,两个中心字符一定相等
			mid1 := n/2 - 1
			mid2 := n / 2

			left = mid1
			right = mid2

			for left > 0 && s[left-1] == s[mid1] {
				left--
			}

			for right+1 < n && s[right+1] == s[mid1] {
				right++
			}
		}

		for i := left; i <= right; i++ {
			ans = append(ans, i)
		}
	}

	return ans
}

func main() {
	in := bufio.NewReader(os.Stdin)

	var s string
	fmt.Fscan(in, &s)

	ans := solve(s)

	out := bufio.NewWriter(os.Stdout)
	defer out.Flush()

	for i, x := range ans {
		if i > 0 {
			fmt.Fprint(out, ",")
		}
		fmt.Fprint(out, x)
	}
}

C语言

cpp 复制代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

bool check(const char* s, int del) {
    int l = 0, r = strlen(s) - 1;

    while (l < r) {
        if (l == del) {
            l++;
            continue;
        }

        if (r == del) {
            r--;
            continue;
        }

        if (s[l] != s[r]) return false;

        l++;
        r--;
    }

    return true;
}

int* solve(const char* s, int* returnSize) {
    int n = strlen(s);
    int* ans = (int*)malloc(sizeof(int) * n);
    int count = 0;

    int l = 0, r = n - 1;

    // 找第一处不匹配
    while (l < r && s[l] == s[r]) {
        l++;
        r--;
    }

    if (l < r) {
        // 原字符串不是回文,只可能删除 l 或 r
        if (check(s, l))
            ans[count++] = l;

        if (check(s, r))
            ans[count++] = r;
    } else {
        // 原字符串本身是回文
        // 合法删除位置就是包含中心的连续相同字符区间
        int left, right;

        if (n % 2 == 1) {
            // 奇数长度,中心是 n / 2
            int mid = n / 2;
            left = right = mid;

            while (left > 0 && s[left - 1] == s[mid])
                left--;

            while (right + 1 < n && s[right + 1] == s[mid])
                right++;
        } else {
            // 偶数长度,两个中心字符一定相等
            int mid1 = n / 2 - 1;
            int mid2 = n / 2;

            left = mid1;
            right = mid2;

            while (left > 0 && s[left - 1] == s[mid1])
                left--;

            while (right + 1 < n && s[right + 1] == s[mid1])
                right++;
        }

        for (int i = left; i <= right; i++)
            ans[count++] = i;
    }

    *returnSize = count;
    return ans;
}

int main() {
    char s[100005];
    scanf("%s", s);

    int size;
    int* ans = solve(s, &size);

    for (int i = 0; i < size; i++) {
        if (i > 0) printf(",");
        printf("%d", ans[i]);
    }

    free(ans);
    return 0;
}
相关推荐
玖玥拾2 小时前
LeetCode 125 验证回文串
算法·leetcode
Asize9 小时前
146. LRU 缓存
算法
Asize9 小时前
543. 二叉树的直径
算法
lemon_sjdk9 小时前
ObjectProperty
java·开发语言·算法
Zentceh9 小时前
AI-ISP在夜视机芯中的应用:从传统ISP到PixelClean全彩夜视的进化
人工智能·科技·算法·计算机视觉·车载系统·视频·智能硬件
xier_ran10 小时前
【infra之路】AWQ 详解:激活感知权重保护,让 W4A16 量化精度超越 GPTQ
线性代数·算法·机器学习·量化·infra
亦皓ai10 小时前
AI时代后端工程(二):AI把代码写得越来越快,我却越来越不敢让它直接开工了
人工智能·算法·机器学习·搜索引擎·transformer
玖玥拾11 小时前
LeetCode 392 判断子序列
笔记·算法·leetcode
重生之后端学习13 小时前
239. 滑动窗口最大值[困难]✅
java·数据结构·算法·leetcode·职场和发展