题目链接

小牛的作业

题目描述

给定两个仅包含小写英文字母的字符串 。可以对字符串 执行两种操作:

  1. 删除 中的任意一个字符。
  2. 中的任意一个字符修改为任意一个小写字母。

要求计算,将字符串 转变为一个新字符串 所需的最小操作次数,使得 中所有字符的出现次数与 中完全相同。若不可能,则输出 -1。

解题思路

这道题的核心是匹配字符的频率,而不是字符串本身。因此,字符的顺序是无关紧要的,我们可以通过频率统计来解决问题。

  1. 判断无解情况: 我们的目标是得到一个新字符串 ,它的字符频率与 相同。这意味着 的总长度必须等于 的总长度,即 。 我们能对 进行的操作只有“删除”(使长度减一)和“修改”(长度不变),我们无法增加 的长度。 因此,如果初始字符串 的长度就小于 的长度(),我们永远无法得到长度为 。这种情况下,任务不可能完成,应输出 -1。

  2. 计算最小操作次数: 在有解的情况下(),问题就转化为:如何用最少的操作,将 的字符频率分布调整成和 一样。 我们可以使用两个大小为 26 的数组 freq_s 和 freq_t 来分别统计两个字符串中每个字母的出现次数。

    解题的关键在于找出 中有多少字符可以“直接复用”来满足 的需求。

    • 对于任意一个字符 ('a' 到 'z'), 中有 freq_s[c] 个,而 需要 freq_t[c] 个。
    • 那么,我们最多可以保留 个字符 ,这些字符无需任何操作。

    将所有 26 个字母可以保留的数量相加,就得到了两个字符串频率分布的“交集”,也就是不需要操作的字符总数

    字符串 的总长度是 。其中, 个字符被我们保留了。那么,剩下的 个字符就是 中“多余的”、“无法直接复用”的字符。

    对于这部分多余的字符,我们必须对它们每一个都进行一次操作(要么是删除,要么是修改),才能最终满足 的频率要求。因此,最小操作次数就等于这部分多余字符的数量。

    最终公式:

代码

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

using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    string s, t;
    cin >> s >> t;

    if (s.length() < t.length()) {
        cout << -1 << endl;
        return 0;
    }

    vector<int> freq_s(26, 0);
    for (char c : s) {
        freq_s[c - 'a']++;
    }

    vector<int> freq_t(26, 0);
    for (char c : t) {
        freq_t[c - 'a']++;
    }

    int common_chars = 0;
    for (int i = 0; i < 26; ++i) {
        common_chars += min(freq_s[i], freq_t[i]);
    }

    cout << s.length() - common_chars << endl;

    return 0;
}
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.nextLine();
        String t = sc.nextLine();

        if (s.length() < t.length()) {
            System.out.println(-1);
            return;
        }

        int[] freq_s = new int[26];
        for (char c : s.toCharArray()) {
            freq_s[c - 'a']++;
        }

        int[] freq_t = new int[26];
        for (char c : t.toCharArray()) {
            freq_t[c - 'a']++;
        }

        int common_chars = 0;
        for (int i = 0; i < 26; i++) {
            common_chars += Math.min(freq_s[i], freq_t[i]);
        }

        System.out.println(s.length() - common_chars);
    }
}
import sys

def solve():
    s = sys.stdin.readline().strip()
    t = sys.stdin.readline().strip()

    if len(s) < len(t):
        print(-1)
        return

    freq_s = [0] * 26
    for char in s:
        freq_s[ord(char) - ord('a')] += 1
    
    freq_t = [0] * 26
    for char in t:
        freq_t[ord(char) - ord('a')] += 1

    common_chars = 0
    for i in range(26):
        common_chars += min(freq_s[i], freq_t[i])
        
    print(len(s) - common_chars)

solve()

算法及复杂度

  • 算法: 贪心、频率统计
  • 时间复杂度: 我们需要遍历字符串 来统计频率,时间复杂度为 。之后计算公共字符数需要遍历一次大小为 26 的频率数组,是常数时间。所以总的时间复杂度为
  • 空间复杂度: 我们使用了两个大小为 26 的数组来存储频率,因此空间复杂度为 (因为字母表大小是常数)。