首页 > 试题广场 >

字母交换

[编程题]字母交换
  • 热度指数:3596 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解

【编码题】字符串S由小写字母构成,长度为n。定义一种操作,每次都可以挑选字符串中任意的两个相邻字母进行交换。询问在至多交换m次之后,字符串中最多有多少个连续的位置上的字母相同?



输入描述:
第一行为一个字符串S与一个非负整数m。(1 <= |S| <= 1000, 1 <= m <= 1000000)


输出描述:
一个非负整数,表示操作之后,连续最长的相同字母数量。
示例1

输入

abcbaa 2

输出

2

说明

使2个字母a连续出现,至少需要3次操作。即把第1个位置上的a移动到第4个位置。
所以在至多操作2次的情况下,最多只能使2个b或2个a连续出现。
Java DP。
分别处理每种字符。
dp[i][j] 表示对于某种字符来说,从第 i 个到第 j 个 该字符如果连续,需要最少移动几次。

dp[i][j] =dp[i + 1][j - 1] + ls.get(j) - ls.get(i) - 1 - (j - i - 1);
递推式的意思是:从i+1~j-1已经连续好了,再把 i 和 j 也变成连续的,需要这两个位置的差(ls.get(j) - ls.get(i) - 1),减去里面已经有的(i - j - 1)。
import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String s = in.next();
        int m = in.nextInt();
        Map<Character, List<Integer>> map = new HashMap<>();
        for (int i = 0; i < s.length(); ++i) {
            map.computeIfAbsent(s.charAt(i), k -> new ArrayList<>()).add(i);
        }
        int ans = 0;
        for (List<Integer> ls: map.values()) {
            int n = ls.size();
            int[][] dp = new int[n][n];
            for (int i = n - 1; i >= 0; --i) {
                for (int j = i + 1; j < n; ++j) {
                    dp[i][j] =dp[i + 1][j - 1] + ls.get(j) - ls.get(i) - 1 - (j - i - 1);
                    if (dp[i][j] <= m) ans = Math.max(ans, j - i + 1);
                }
            }
        }
        System.out.println(ans);
    }
}


编辑于 2023-12-21 21:52:18 回复(0)
Heap 天 下 第 一
import java.util.*;

public class Main{
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        String str = sc.next();
        int m = sc.nextInt();
        HashMap<Character, ArrayList<Integer>> table = new HashMap<>();
        for(int i=0;i<str.length();i++){
            if(!table.containsKey(str.charAt(i))){
                int finalI = i;
                table.put(str.charAt(i),new ArrayList<Integer>(){{add(finalI);}});
            }else{
                table.get(str.charAt(i)).add(i);
            }
        }
        PriorityQueue<Integer> res = new PriorityQueue<Integer>(Comparator.reverseOrder());
        for(Character x: table.keySet()){
            PriorityQueue<Integer> temp = new PriorityQueue<Integer>(Comparator.reverseOrder());
            for(int i = 0;i<table.get(x).size();i++){
                int time = m;
                int lcount = 1,rcount = 1;
                PriorityQueue<Integer> dis = new PriorityQueue<Integer>(new Comparator<Integer>(){
                    @Override
                    public int compare(Integer j, Integer k) {
                        if (Math.abs(j)<Math.abs(k)){
                            return -1;
                        }else{
                            return 1;
                        }
                    }
                });
                for(int j=0;j<table.get(x).size();j++){
                    dis.add(table.get(x).get(j)-table.get(x).get(i));
                }
                while (!dis.isEmpty()){
                    int test = dis.poll();
                    if(test <0 && Math.abs(test)<=time){
                        int cost = (test+lcount);
                        lcount ++;
                        time += cost;
                    }else if(test>0 && test <=time){
                        int cost = (test-rcount);
                        rcount ++;
                        time -= cost;
                    }
                }
                temp.add(lcount+rcount-1);
            }
            res.add(temp.poll());
        }
        System.out.println(res.poll());
    }
}


发表于 2020-01-13 17:24:04 回复(0)
思路:
  对于每个字母,将其出现的位置存储在一维数组position[]中。递归遍历这些位置的所有组合(可通过两个for循环嵌套实现,这样就不会重复)。
  递归函数dfsSwichCharacter(i,j,position)返回的是实现position[i]到position[j]之间的相同字母连续排列需要移动的次数。这里注意状态转移方程,当j==i+1时说明字符串s中position[i]和position[j]之间不可能再有该字母,所以移动次数就是坐标之差减一;当j>i+1时移动次数相当于是把position[j]的字母移到position[i]隔壁的次数减去这两个位置之间该字母的个数。
  如果此时交换次数没有超出限制,则检查此时交换后相同连续出现的字母是不是最长并更新answer = Math.max(answer, q-p+1)。
  最后,每个字母在限制内移动次数得到的最大长度保存在length[]数组中。
import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String[] strs = sc.nextLine().split(" ");
        String s = strs[0];
        int count = Integer.parseInt(strs[1]);
        int len = s.length();
        int[][] record = new int[len][26]; //记录字符串s的每个位置和26个字母的关系
        int[] length = new int[26]; //记录每个字母在规定交换次数内最长的相同字母数量
        for(int i = 0; i < len; i++){
            record[i][s.charAt(i)-'a'] = 1;
        }
        for(int i = 0; i < 26; i++){
            int[] position = new int[len]; //对于每个字母,记录字符串s中出现该字母的位置
            int k = 0; //字符串s中出现该字母k次
            for(int j = 0; j < len; j++){
                if(record[j][i] == 1){
                    position[k] = j;
                    k += 1;
                }
            }
            if(k == 0){
                length[i] = 0;
            }else if(k == 1){
                length[i] = 1;
            }else{
                int answer = Integer.MIN_VALUE;
                for (int p = 0; p < k; p++){
                    for (int q = p+1; q < k; q++){
                        int res = dfsSwichCharacter(p, q, position);
                        if(res <= count){ //在规定交换次数内,更新连续的某个相同字母数量
                            answer = Math.max(answer, q-p+1);
                        }
                    }
                }
                length[i] = answer;
            }
        }
        int result = Integer.MIN_VALUE;
        for(int i : length){
            result = Math.max(result, i);
        }
        System.out.println(result);
    }
    
    public static int dfsSwichCharacter(int i, int j, int[] position){
        if(i == j){
            return 0;
        }else if(i+1 == j){ //说明字符串s中position[i]和position[j]之间不可能再有该字母,所以移动次数就是坐标之差减一
            return position[j] - position[i] - 1;
        }else{ //移动次数相当于是把position[j]的字母移到position[i]隔壁的次数减去这两个位置之间该字母的个数
            return dfsSwichCharacter(i+1, j-1, position) + position[j]-position[i]-1 - (j-i-1);
        }
    }
}


发表于 2019-08-13 05:49:18 回复(0)
import  java.util.*;


public class Main{
    
    //大神帮我瞅瞅结果都对可是就是提交不对
    public static void Main(String[] args){
      int[][] dp = new int[1500][1500];
int ans = 1;
@SuppressWarnings("resource")
Scanner input = new Scanner(System.in);
String str = input.next();
int x = input.nextInt();
Map<Character, List<Integer>> map = new HashMap<>();
char[] ch = str.toCharArray();
for (int i = 0; i < ch.length; i++) {
if (map.containsKey(ch[i])) {
List<Integer> a = map.get(ch[i]);
a.add(i);
} else {
List<Integer> a = new ArrayList<>();
a.add(i);
map.put(ch[i], a);
}
}
for (Character key : map.keySet()) {
List<Integer> d = map.get(key);
for(int i=0;i<d.size();i++){
for(int j=0;j<d.size();j++){
if(i==j){
dp[i][j]=0;
}else{
dp[i][j]=1001;
}
}
}
for (int i = d.size() - 1; i >= 0; i--) {
for (int j = i + 1; j < d.size(); j++) {
int dis = d.get(j) - d.get(i) - (j - i);
if (i + 1 != j){
dis += dp[i + 1][j - 1];
}
dp[i][j] = (dp[i][j]-dis)>0?dis:dp[i][j];
if (dp[i][j] <= x){
ans = (ans>(j - i + 1))?ans:(j-i+1);
}
}
}
}
System.out.println(ans);


    }
}
发表于 2019-02-22 21:40:45 回复(0)
// Java

public static void solv(String s, int m){

    Map<Character, List> map = new HashMap<>(26);

    for (int i = 0; i < s.length(); i++){

        char c = s.charAt(i);

        List list = map.get(c);

        if (list == null)

            map.put(c, list = new ArrayList<Integer>(100));

        list.add(i);

    }

    int maxLen = 1;

    for (Map.Entry<Character, List> entry : map.entrySet()){

        List<Integer> arrayList = entry.getValue();

        for (int i = 0; i < arrayList.size(); i++){

            int ctr = arrayList.get(i);

            int []move = new int[arrayList.size()];

            // 获取 move[],表示每个相同字母到中心点 ctr 需要移动的最少步数

            for (int j = 0; j < arrayList.size(); j++)

                move[j] = (Math.abs(arrayList.get(j) - ctr) -

                        Math.abs(i - j));

            // 排序后,选择移动代价最少的前 k + 1

            Arrays.sort(move);

            int sum = 0;

            for (int k = 0; k < move.length; k++){

                sum += move[k];

                if (sum > m)

                    break;

                if (k + 1 > maxLen)

                    maxLen = k + 1;

            }

        }

    }

    System.out.println(maxLen);

}

https://blog.csdn.net/qq_34385263/article/details/82026285
发表于 2018-08-24 23:21:37 回复(0)