字符串思维题练习 DAY1(CF691B , CF706C , CF1555D)

字符串思维题练习 DAY1

学术小群:545214567(讨论牛客系列赛,codeforces,atcoder等)

Problem - 691B - Codeforces

CF691 B. s-palindrome(思维 + 镜像回文)

大意:定义镜像回文串为中心镜像对称的串 , 给出一个串询问是否是镜像回文串。

思路:可以按照类马拉车的思路重新定义匹配方式 ,匹配即可 , 要注意的是对于长度为奇数的字符串中心的字符要跟自己匹配 , 即本身镜像对称。(小写 i , 和小写 m 本身并不镜像对称)

时间复杂度 O ( n ) 时间复杂度O(n) 时间复杂度O(n)

#include
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define int long long
const int N = 2e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;

map<char , char> mp;
string s;

signed main(){

	mp['A'] = 'A';
	mp['b'] = 'd';
	mp['d'] = 'b';
	mp['H'] = 'H';
	mp['I'] = 'I';
	mp['M'] = 'M';
	mp['O'] = 'O';
	mp['o'] = 'o';
	mp['p'] = 'q';
	mp['q'] = 'p';
	mp['T'] = 'T';
	mp['U'] = 'U';
	mp['V'] = 'V';
	mp['v'] = 'v';
	mp['w'] = 'w';
	mp['W'] = 'W';
	mp['X'] = 'X';
	mp['x'] = 'x';
	mp['Y'] = 'Y';
	
	cin >> s;
	int n = s.size();
	s = '?' + s;
	bool tag = 1;
	if(n & 1) {
		int mid = (n + 1) / 2;
		if(mp[s[mid]] != s[mid]) tag = 0;
		for(int i = 1 ; i <= n / 2 ; i ++) {
			if(mp[s[i]] != s[n - i + 1]) tag = 0;
		}
	} else {
		for(int i = 1 ; i <= n / 2 ; i ++) {
			if(mp[s[i]] != s[n - i + 1]) tag = 0;
		}
	}
	
	if(tag) cout << "TAK";
	else cout << "NIE";

	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);

CF706 C. Hard problem(排序 + dp)

Problem - C - Codeforces

大意:给出 n 个字符串 , 每个字符串都可以消耗一定的花费去翻转 , 问是否可以翻转一些字符串使得所有字符串字典序升序排序并求出最小花费。

思路:非常小清新的一道动态规划题 , 等等 , 怎么是个动态规划题?让我们一点点分析。

我们思考如何转化这个问题 , 我们可以把翻转和未翻转的字符串都放到一起排个序 并 保留它的原始序号 和 花费。排序后自然就满足了字典序升序排序的条件 , 这样问题就转化成了在当前的序列中在不改变顺序的前提下选择出一个原始序号等于 [1 - n] 的子集并满足花费最小。考虑动态规划。

d p [ i ] 表示以  1 − i  子集的最小花费 dp[i] 表示以 ~1-i~ 子集的最小花费 dp[i]表示以 1i 子集的最小花费

定义出状态转移也很显然

d p [ i ] = m i n ( d p [ i ] , d p [ i − 1 ] + v a l [ i ] ) dp[i] = min(dp[i] ,dp[i-1]+val[i]) dp[i]=min(dp[i],dp[i1]+val[i])

显然答案就是 dp[n]

时间复杂度 O ( n l o g n ) 时间复杂度O(nlogn) 时间复杂度O(nlogn)

#include
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define int long long
const int INF = 9e18;
const int N = 2e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;

int n;
int v[N];
string s;
vector<tuple<string , int , int>> ve;

signed main(){

	IOS
	cin >> n;
	for(int i = 1 ; i <= n ; i ++) {
		cin >> v[i];
	} 
	for(int i = 1 ; i <= n ; i ++) {
		cin >> s;
		ve.emplace_back(s , i , 0);
		reverse(s.begin() , s.end());
		ve.emplace_back(s , i , v[i]);
	}
	
	sort(ve.begin() , ve.end());
	
	vector<int>dp(n + 1 , INF);
	dp[0] = 0;
	for(int i = 0 ; i < n + n ; i ++) {
		int now = get<1>(ve[i]);
		int val = get<2>(ve[i]);
		dp[now] = min(dp[now] , dp[now - 1] + val);
	}
	
	cout << (dp[n] == INF ? -1 : dp[n]) ;

	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);

CF1555 D. Say No to Palindromes(前缀和)

Problem - D - Codeforces

大意:给出一个字符串 ,字符串中只含有 abc 三个字符 。 定义完美字符串为当前字符串中所有长度大于2的子串都非回文串 。 给出 q 次询问 ,每次询问使得当前区间的字符串变成完美字符串的最小花费 ,定义一次花费为一次字符替换。

思路:手模一下不难发现 , 完美字符串一共有六种 , 开头的三个字符分别对应着 “abc” 的六种排列。不难想出一个暴力思路就是把区间的字符串和这六种字符串进行对比 , 计算最小花费。

时间复杂度: O ( ∑ i = 1 m l e n i ) 时间复杂度:O(\sum_{i=1}^{m}len_i) 时间复杂度:O(i=1mleni)

显然最坏复杂度是 O(nm) , 不能接受 , 考虑优化。

再次观察完美字符串 ,不难发现完美字符串都是以 3 为周期的字符串 , 考虑查询区间所有 %3 = 0 ,%3 = 1 , %3 = 2 的位置字符相同 , 枚举这三个位置应填的字母 , 然后前缀和数组存储查询区间三个位置三种字母的个数 , 即可完成优化。

时间复杂度 O ( 36 m ) 时间复杂度O(36m) 时间复杂度O(36m)

#include
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define int long long
const int INF = 9e18;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;

int n , q , l , r;
string s , ss[7];
string st = "abc";
int cnt[N][3][3];

/*
前缀 i 三种位置 , 三个字符的个数
*/

signed main(){

	IOS
	cin >> n >> q;
	cin >> s;
	s = '?' + s;
	
	for(int i = 1 ; i <= n ; i ++) {
		cnt[i][0][0] = cnt[i - 1][0][0] + 1 * (s[i] == 'a') * (i % 3 == 1);
		cnt[i][0][1] = cnt[i - 1][0][1] + 1 * (s[i] == 'b') * (i % 3 == 1);
		cnt[i][0][2] = cnt[i - 1][0][2] + 1 * (s[i] == 'c') * (i % 3 == 1);
		cnt[i][1][0] = cnt[i - 1][1][0] + 1 * (s[i] == 'a') * (i % 3 == 2);
		cnt[i][1][1] = cnt[i - 1][1][1] + 1 * (s[i] == 'b') * (i % 3 == 2);
		cnt[i][1][2] = cnt[i - 1][1][2] + 1 * (s[i] == 'c') * (i % 3 == 2);
		cnt[i][2][0] = cnt[i - 1][2][0] + 1 * (s[i] == 'a') * (i % 3 == 0);
		cnt[i][2][1] = cnt[i - 1][2][1] + 1 * (s[i] == 'b') * (i % 3 == 0);
		cnt[i][2][2] = cnt[i - 1][2][2] + 1 * (s[i] == 'c') * (i % 3 == 0);		
	}
	
	for(int i = 1 ; i <= 6 ; i ++) {
		ss[i] = st;
		next_permutation(st.begin() , st.end());
	}
	
	for(int i = 1 ; i <= q ; i ++) {
		cin >> l >> r;
		int minn = INF;
		for(int j = 1 ; j <= 6 ; j ++) {
			int fi = ss[j][0] - 'a';
			int se = ss[j][1] - 'a';
			int th = ss[j][2] - 'a';
			int waste = 0;
			waste += cnt[r][0][se] - cnt[l - 1][0][se];
			waste += cnt[r][0][th] - cnt[l - 1][0][th];
			waste += cnt[r][1][fi] - cnt[l - 1][1][fi];
			waste += cnt[r][1][th] - cnt[l - 1][1][th];
			waste += cnt[r][2][se] - cnt[l - 1][2][se];
			waste += cnt[r][2][fi] - cnt[l - 1][2][fi];
			minn = min(minn , waste);			
		}
		cout << minn << "\n";
	}

	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);
/*
5 1
baacb
2 3
*/

你可能感兴趣的:(算法)