“海康威视杯“ 2022年第十四届四川省大学生程序设计大赛 A题

思路:1.首先遍历字符串,记录各个字符的数目b[t];

            2.再次遍历字符串,如果当前cnt[t]<b[t]/2,那么我们就可以把他放入前半段字符串中,移动次数为(i-size);否则就将其放入后半段字符串中;

            3.最后将后半段字符串根据前半段字符串的顺序得到一个数组,然后通过归并排序来排序,记录的逆序对数;

package Main;

import java.io.*;
import java.util.*;

public class Main1 {
	static long ans=0;//最少交换次数
	static int b[]=new int[26],cnt[]=new int[26];//b数组用来记录字符串中各字符的数量;cnt数组用来记录在遍历过程中当前各字符是否满足一半的数目
	static Vector<Integer> a[]=new Vector[26];//用来记录前半段中字符的下标;
	static Vector<Integer> h=new Vector<>(),hh=new Vector<>();//分别记录前后两段字符串
	static int aa[];//记录后段字符串对应的下标
	public static void main(String[] args) {
		int n=cin.nextInt();
		String s=cin.nextLine();
		
		char c[]=s.toCharArray();
		for(int i=0;i<c.length;i++) {
			int t=c[i]-'a';
			b[t]++;
		}
		
		for(int i=0;i<26;i++)a[i]=new Vector<>();
		
		//将字符串分为前后两段
		for(int i=0;i<c.length;i++) {
			int t=c[i]-'a';
			if(cnt[t]<b[t]/2) {
				int size=0;
				if(!h.isEmpty())
					size=h.size();
				
				cnt[t]++;
				ans+=i-size;
				h.add(t);
//				System.out.println(i+" "+t+" "+size);
				a[t].add(h.size());
			}else hh.add(t);
		}
		

		int id=0;
		aa=new int[hh.size()];
		
		//得到后半段字符串的下表
		for(int i=0;i<hh.size();i++) {
			int t=hh.get(i);
			aa[id++]=a[t].get(0);
			a[t].remove(0);
		}
		
		//归并模板
		mergeSort(aa,0,hh.size()-1);
		
		out.println(ans);
		out.flush();
	}

	private static void mergeSort(int[] a, int i, int j) {
		// TODO Auto-generated method stub
		if(i>=j)return;
		int mid=i+(j-i)/2;
		mergeSort(a,i,mid);
		mergeSort(a,mid+1,j);
		merge(a,i,mid,j);
	}

	private static void merge(int[] a, int left, int mid, int right) {
		// TODO Auto-generated method stub
		int s1=left;
		int s2=mid+1;
		int ret[]=new int[right-left+1];
		int i=0;
		while(s1<=mid&&s2<=right)
		{
			if(a[s2]<a[s1]) {
				ret[i++]=a[s2++];
				ans+=mid-s1+1;//求逆序对数目
			}
			else {
				ret[i++]=a[s1++];
			}
		}
		while(s1<=mid)ret[i++]=a[s1++];
		while(s2<=right)ret[i++]=a[s2++];
		for(int j=0;j<ret.length;j++)
			a[j+left]=ret[j];
	}
	
    //快输模板
	static class FastScanner{
		BufferedReader br;
		StringTokenizer st;
		
		public FastScanner(InputStream in) {
			br=new BufferedReader(new InputStreamReader(in),16384);
			eat("");
		}
		
		public void eat(String s) {
			st=new StringTokenizer(s);
		}
		
		public String nextLine() {
			try {
				return br.readLine();
			}catch(IOException e) {
				return null;
			}
		}
		
		public boolean hasNext() {
			while(!st.hasMoreTokens()) {
				String s=nextLine();
				if(s==null)return false;
				eat(s);
			}
			
			return true;
		}
		
		public String next() {
			hasNext();
			return st.nextToken();
		}
		
		public int nextInt() {
			return Integer.parseInt(next());
		}
		
		public long nextLong() {
			return Long.parseLong(next());
		}
		
		public double nextDouble() {
			return Double.parseDouble(next());
		}
	}
	
	static FastScanner cin=new FastScanner(System.in);
	static FastScanner sc=new FastScanner(System.in);
	static PrintWriter out=new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值