回文字符串构造与字典序优化

118 阅读4分钟

学习笔记:回文字符串构造与字典序优化

一、题目概述

小C手中有一个由小写字母组成的字符串 ss,她需要构造一个回文字符串 tt,满足以下条件:

  1. tt 是回文字符串,即从左到右与从右到左相同。
  2. tt 的字典序要小于 ss,并且在所有符合条件的字符串中,字典序尽可能大。
  3. 如果无法构造出符合条件的回文字符串,返回 -1

二、解题思路

1. 回文字符串的构造:

回文字符串的特点是其前半部分决定了后半部分。也就是说,如果我们确定了前半部分的字符,后半部分就可以直接对称得到。

  • 例如,如果 s="abc"s = "abc",我们可以尝试构造回文字符串 "aba",将字符串前半部分的字符反转并作为后半部分。

2. 字典序要求:

  • 我们希望 tt 的字典序尽可能大,但必须小于原字符串 ss。
  • 由于回文字符串的特性,我们只需要调整前半部分的字符来满足字典序条件。通过从后向前调整字符,使其字典序尽可能大但又小于 ss。

3. 解题步骤:

  1. 初步构造回文字符串:

    • 根据 ss 的前半部分构造一个回文字符串 tt,将其后半部分与前半部分对称。
  2. 检查字典序:

    • 如果初步构造的回文字符串 tt 已经小于 ss,直接返回它。
  3. 调整前半部分:

    • 如果初步构造的回文字符串 tt 不满足字典序小于 ss,则从后向前调整前半部分的字符。每次将字符减小并重新构造回文字符串,直到找到符合条件的结果。
  4. 边界条件:

    • 如果无法调整字符(即所有字符已经是最小的字典序),返回 -1

三、代码实现

#include <iostream>
#include <string>

std::string make_palindrome_less_than(std::string s) {
    int n = s.size();
    std::string t = s;

    // Step 1: Construct an initial palindrome by copying the first half of s
    for (int i = 0; i < n / 2; ++i) {
        t[n - i - 1] = s[i];
    }

    // Step 2: Check if this palindrome is already less than s
    if (t < s) {
        return t;
    }

    // Step 3: Try to adjust the first half of the string to get the largest possible palindrome less than s
    for (int i = n / 2 - 1; i >= 0; --i) {
        if (t[i] > 'a') {
            // Decrease the character at position i
            t[i]--;
            // Rebuild the palindrome
            for (int j = i + 1; j < n / 2; ++j) {
                t[j] = 'z'; // Maximize the characters on the right part of the palindrome
            }
            for (int j = 0; j <= i; ++j) {
                t[n - j - 1] = t[j]; // Mirror the changes on the second half
            }
            return t;
        }
    }

    // Step 4: If no valid palindrome is found
    return "-1";
}

int main() {
    std::string s;
    std::cin >> s;

    std::string result = make_palindrome_less_than(s);
    std::cout << result << std::endl;

    return 0;
}

代码解析:

  1. 初步构造回文字符串:

    • 根据 ss 的前半部分构造回文字符串 tt。
    • 如果 tt 已经小于 ss,直接返回 tt。
  2. 调整回文字符串:

    • 从回文字符串的后半部分开始,逐步减少前半部分的字符,并重新构造回文字符串,确保字典序尽可能大但小于 ss。
  3. 返回结果:

    • 如果找到符合条件的回文字符串,则返回它;如果无法找到,则返回 -1

四、复杂度分析

时间复杂度:

  • 构造回文字符串的时间复杂度是 O(n)O(n),其中 nn 是字符串的长度。
  • 调整前半部分的字符时,最坏的情况下我们需要对每个字符进行调整,时间复杂度仍为 O(n)O(n)。

因此,整体时间复杂度为 O(n)O(n)

空间复杂度:

  • 我们仅使用了一个额外的字符串 tt 来存储回文字符串,因此空间复杂度为 O(n)O(n)

五、测试样例分析

样例1:

输入:s = "abc"
输出:"aba"
  • 初步构造回文字符串为 "aba",其字典序小于 "abc",符合条件。

样例2:

输入:s = "cba"
输出:"cac"
  • 初步构造回文字符串为 "cbc",字典序不小于 "cba"。调整前半部分字符得到 "cac",符合条件。

样例3:

输入:s = "aaa"
输出:"-1"
  • 无法构造出符合条件的回文字符串,因为所有字符都是 'a',已经是字典序最小。

六、学习心得

1. 回文字符串的特性:

  • 通过利用回文字符串的对称性,我们能够减少问题的复杂度,避免暴力枚举所有可能的回文字符串。

2. 字典序优化:

  • 通过从后向前调整字符,确保每次修改都能使得字符串尽可能大,同时不违反字典序小于原字符串的约束。

3. 编程技巧:

  • 在字符串问题中,学会合理地利用字符操作和对称性优化算法,能大幅提高效率。

七、总结

本题考察了如何在回文字符串中进行优化,确保生成的字符串字典序尽可能大同时又满足小于原字符串的约束。通过对回文字符串的结构理解,能够有效地解决问题,并通过调整字符的方式获得最佳解。