字符串拼接

一、题目描述

给定 M 个小写英文字符(0 < M ≤ 30),需从中选取字符拼接成长度为 N 的字符串(0 < N ≤ 5),满足以下规则:

  1. 每个字符只能使用一次;
  2. 字符串中相同字符不能相邻;
  3. 若输入非法(如 N > M、字符列表为空等)或无法拼接出满足条件的字符串,返回 0

计算出给定的字符列表能拼接出多少种满足条件的字符串

二、输入输出描述

输入描述
  • 给定的字符列表和结果字符串长度,中间使用空格(" ")拼接
输出描述
  • 满足条件的字符串个数

三、示例

|----|------------|
| 输入 | aab 2 |
| 输出 | 2 |
| 说明 | 只能构成ab,ba。 |

|----|--------------------------|
| 输入 | abc 2 |
| 输出 | 6 |
| 说明 | 可以构成:ab ac ba bc ca cb 。 |

四、解题思路

1. 核心思想

通过递归 + 回溯 生成所有符合规则的字符组合,利用HashSet自动去重,最终统计唯一组合的数量 ------ 核心是 "遍历选字符 + 标记去重 + 回溯恢复 + Set 去重",确保生成的组合满足 "长度达标、无重复字符、相邻字符不同" 的规则。

2. 问题本质分析
  • 表层问题:统计从源字符串中选指定长度字符、无重复字符、相邻字符不同的唯一组合数;
  • 深层问题:
    1. 组合生成问题:本质是 "无重复的排列组合"(选指定长度、字符不重复);
    2. 去重需求:源字符串可能有重复字符(如"aab"),需过滤重复的组合(如"ab"仅算一次);
    3. 回溯的必要性:递归选字符后需恢复标记,才能生成所有可能的组合(如选a后取消标记,才能选b生成ba);
    4. 相邻字符判断:虽因 "无重复字符" 可省略,但代码保留该逻辑,适配更通用的场景(如允许重复选字符时,避免相邻相同)。
3. 核心逻辑
  • 标记去重:用used数组标记已选字符,避免同一组合中重复选取;
  • 递归拼接:逐位拼接字符,直到组合长度达标;
  • 回溯恢复:递归返回后取消字符标记,保证后续遍历能生成其他组合;
  • 自动去重:用HashSet存储组合,过滤源字符串重复字符导致的重复组合;
  • 相邻判断:额外过滤相邻字符相同的情况,增强规则适配性。
4. 步骤拆解
  1. 输入处理
    • 拆分输入为源字符串和目标组合长度;
  2. 初始化工具
    • 创建HashSet存储唯一组合,创建used数组标记字符使用状态;
  3. 递归生成组合
    • 终止条件:组合长度等于目标长度,加入 Set;
    • 遍历源字符串字符:
      1. 跳过已使用的字符或与组合最后一位相同的字符;
      2. 标记字符为已使用,递归拼接字符;
      3. 回溯:递归返回后取消字符标记;
  4. 统计结果
    • 返回HashSet的大小,即唯一组合的数量。

五、代码实现

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

public class Main {
    public static void main(String[] args) {
        // 创建一个Scanner对象来读取用户的输入
        Scanner sc = new Scanner(System.in);
        // 读取用户输入的字符串
        String input = sc.nextLine();
        // 将输入的字符串按空格分割为两部分,分别为字符串和长度
        String[] parts = input.split(" ");
        String str = parts[0]; // 获取输入的字符串
        int length = Integer.parseInt(parts[1]); // 将输入的长度部分转换为整数

        // 调用countDistinctStrings方法计算满足条件的不同字符串的数量
        int count = countDistinctStrings(str, length);
        // 输出计算结果
        System.out.println(count);
    }

    // 计算满足条件的不同字符串的数量
    public static int countDistinctStrings(String str, int length) {
        // 创建一个HashSet来存储不同的字符串
        HashSet<String> set = new HashSet<>();
        // 创建一个boolean数组来标记字符串中的字符是否已经被使用
        boolean[] used = new boolean[str.length()];
        // 调用generateDistinctStrings方法生成满足条件的不同字符串
        generateDistinctStrings(str, length, "", set, used);
        // 打印生成的所有不同的字符串
        // for(String str1 : set){
           // System.out.println(str1);
        // }
        // 返回不同字符串的数量
        return set.size();
    }

    // 递归生成满足条件的不同字符串
    public static void generateDistinctStrings(String str, int length, String current, HashSet<String> set, boolean[] used) {
        // 当生成的字符串长度等于指定长度时,将其加入到HashSet中
        if (current.length() == length) {
            set.add(current);
            return;
        }

        // 遍历字符串中的字符
        for (int i = 0; i < str.length(); i++) {
            // 判断字符是否已经被使用,或者当前字符与前一个字符相同
            if (used[i] || (current.length() > 0 && current.charAt(current.length() - 1) == str.charAt(i))) {
                continue; // 如果字符已被使用或与前一个字符相同,则跳过当前字符
            }
            used[i] = true; // 标记当前字符为已使用
            // 递归调用生成下一个字符
            generateDistinctStrings(str, length, current + str.charAt(i), set, used);
            used[i] = false; // 取消标记当前字符的使用状态,以便下一次遍历
        }
    }
}
相关推荐
遥远_2 小时前
一次高并发压垮系统的排查与重生(上)
java·微服务·性能优化·高并发·限流·qps
LYFlied2 小时前
【每日算法】LeetCode 279. 完全平方数(动态规划)
前端·算法·leetcode·面试·动态规划
C雨后彩虹2 小时前
ConcurrentHashMap入门:高并发场景的 HashMap替代方案
java·数据结构·哈希算法·集合·hashmap
weixin_425023002 小时前
Spring boot 2.7.18使用knife4j
java·spring boot·后端
产幻少年2 小时前
面试题八股
java
wanghowie2 小时前
01.08 Java基础篇|设计模式深度解析
java·开发语言·设计模式
scx201310042 小时前
20251201换根DP总结
算法·动态规划·换根dp
zd2005723 小时前
STREAMS指南:环境及宿主相关微生物组研究中的技术报告标准
人工智能·python·算法