字符串 2020 蓝桥杯省赛 B 组模拟赛(一)

题目

有一个长度为 LL 的字符串,每个字符是大写字母。如果我们把 AA 看做 00 ,BB 看做 11 ,CC 看做 22 … ZZ 看做 2525,那么我们就得到了一个 2626 进制的数字串。

我们可以对这个字符串做一个操作:将两个位置的字母进行交换。这样得到了一个新的数字串。

现在有一个十进制整数 MM ,请判断是否可以通过做至多一次(可以不做)操作,使得得到的字符串是 MM 的倍数。
输入格式
第一行一个只包含大写字母的字符串。

第二行一个整数 MM 。

输出格式
如果初始串就可以,那么输出 “0 0”(不加引号)

如果通过一次操作可以,请输出交换的两个位置的标号(标号小的在前,从 11 开始)。如果有多解,输出字典序最小的。

如果做不到,那么输出 “-1 -1”(不加引号)

数据范围
字符串长度为 LL 。

对于 30%30% 的数据: 1 \leq L \leq 10, 1 \leq M \leq 1001≤L≤10,1≤M≤100

对于 50%50% 的数据:除前面 30%30% 外, 1 \leq L \leq 500, M = 51≤L≤500,M=5 或 2525 或 2626

对于 100%100% 的数据: 1 \leq L \leq 2, 000, 1 \leq M \leq 200,0001≤L≤2,000,1≤M≤200,000

样例输入

NETTLE
35

样例输出

1 2

思路

巨渣本渣做出这道题,有一种学到了的赚到感
一开始我是想着把26进制转化为十进制储存来比较
后来一看数据范围2000???unsigned long long都不够爆的

后来WA声一片了一晚上之后终于:
1.可以每加一次就取一次模,这样就不会溢出了
2.可以先把每个位置的权值用一个数组记录下来,这样到时候算值的时候就可以直接拿对应位置的数字乘上权值,这个权值也可以一边求一遍取模
3.在n²暴力过程中,不需要每次都计算字符串的和,可以直接利用前面求到的权值,计算要交换位置的两个字符之间的差值,再用差值加上原始字符串的值对m取余

代码

#include
#define maxn 2000
#define ll long long
using namespace std;

int main(){
	string s;
	int w[maxn];//w[]数组记录每个位置的权值
	int m;
	cin>>s>>m;
	
	int sum = 0;	//计算原始字符串的和
	for(int i=0; i<s.size(); i++)
		sum = (sum*26+(s[i]-'A')) % m;
	
	//注意0是任何数的倍数
	if(!m || !sum){
		cout<<"0 0";
		return 0;
	}
	
	//求每个位置的权值
	w[s.size()-1] = 1;
	for(int i=s.size()-2; i>=0; i--)
		w[i] = (w[i+1]*26)%m;
	
	//暴力n²求解
	for(int i=0; i<s.size(); i++){
		for(int j=i+1; j<s.size(); j++){
			//tmp计算的是两个位置交换之后和原始字符串之间的差值
			int tmp = (s[i] - s[j])*w[j] + (s[j]-s[i])*w[i];
			//如果原始值+差值可以整除m,则直接输出
			if((tmp + sum) %m==0){
				cout<<i+1<<" "<<j+1;
				return 0;
			}
		}
	}
	//无解输出-1 -1
	cout<<"-1 -1";
	return 0;
} 

你可能感兴趣的:(字符串)