字符串思维题练习 DAY3(CF1511 D , CF1537 E2 , CF165 C)

字符串思维题练习 DAY3(CF1511 D , CF1537 E2 , CF165 C)

Problem - D - Codeforces

CF 1511 D. Min Cost String(思维)

大意:定义一个字符串的花费为 s[i] = s[j] && s[i + 1] = s[j + 1] , 要求构造一个长度为 n 的字符串使得花费最小。

思路 : 考虑字符串长超过 k^2 的时候一定会产生贡献 , 这是不可避免地 , 考虑最小化不超过 k^2 的时候的贡献 , 手摸一下以及观察样例一可以找到这个构造方法 , 即

a a b a c a d   b b c b d   c c d   d ( k = 4 ) aabacad~bbcbd~ccd~d(k = 4) aabacad bbcbd ccd dk=4

a a b a c a d a e   b b c b d b e   c c d c e   d d e   e ( k = 5 ) aabacadae~bbcbdbe~ccdce~dde~e(k = 5) aabacadae bbcbdbe ccdce dde ek=5

#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 , m;
string ans;
signed main(){

	cin >> n >> m;

	if(m == 1) {
		ans = 'a';
	} else {
		for(int i = 1 ; i <= m - 1 ; i ++) {
			for(int j = i ; j <= m ; j ++) {
				if(j != m) {
					ans += char ('a' - 1 + j);
					ans += char ('a' - 1 + i);
				} else {
					ans += char ('a' - 1 + j);
				}
			}
		}
		ans += char('a' - 1 + m);
	}
	
//	cout << ans << "\n";
	
	string now;
	
	if(ans.size() > n) {
		ans.resize(n);
	} else {
		int cnt = ((n - 1) / (int)ans.size()) + 1;
		for(int i = 1 ; i <= cnt ; i ++) now += ans;
		now.resize(n);
		ans = now;
	}
	
	cout << ans ;
	

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

CF 1537 E2. Min Cost String(LCP)

Problem - E2 - Codeforces

大意:给出一个字符串 s , 两种操作 , 1:删除 s 末尾的一个字母 , 2:s := s + s ;求操作任意次后长度为 k 的最小字典序字符串。

思路:可以证明结果一定是由当前串的某一个前缀多次复制得到的 , 那么是哪一个前缀呢 。我们只需贪心的从小到大比较前缀[1 - i] 字典序是否小于 [i + 1 - 2 * i]即可。

如何比较两个区间字典序大小? 只需求解两个字符串的 LCP 长度并比较下一位即可。

如何求解 LCP 呢 , 给出两个做法

1. 哈希二分求LCP

复杂度 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;

#define ull unsigned long long
const int Base = 131;
ull base[N] , h[N];
inline void init_hash(string s){
	int n = s.size();
	h[0] = 0;
	for(int i = 1 ; i <= n ; i ++) h[i] = h[i - 1] * Base + s[i - 1];
	base[0] = 1;
	for(int i = 1 ; i <= n ; i ++) base[i] = base[i - 1] * Base;
}

inline ull get(int l,int r){
	return h[r] - h[l] * base[r - l];
}

int n , m , id = -1;
string s , ans , res , nxt;

signed main(){

	cin >> n >> m >> s;
	ans = s;
	s += s;
	n += n;
	init_hash(s);
	s = '?' + s;
	
	for(int i = 1 ; i <= n / 2 ; i ++) {
		int l = 0 , r = i , mid = 0;
		while(l < r) {
			mid = (l + r) >> 1;
			if(get(0 , mid + 1) != get(i , i + mid + 1)) r = mid;
			else l = mid + 1;
		}
		int pos = l;
		if(pos < i && s[pos + 1] < s[i + pos + 1]) {
			id = i; break;
		}
	}
	
	if(id != -1) ans = s.substr(1 , id);
	
	int cnt = (m - 1) / ((int)ans.size()) + 1;
	
	for(int i = 1 ; i <= cnt ; i ++) {
		res += ans;
	}
	
	res.resize(m);
	
	cout << res << "\n";

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

/*
10 11
yyynhtexfv
8 17
dbcadabc
*/

2. 扩展 kmp 求 LCP

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

不难发现后边区间的LCP可以看成后缀的LCP , 用扩展KMP求解即可。

#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 , m;
string s , ans , res , nxt;
int z[N] , id = -1;

void get_z(string s){
	
	int n = s.size();
	s = ' ' + s;
	z[1] = n;
	for(int i = 2 , l = 1 , r = 0 ; i <= n ; i ++){
		if(i <= r) z[i] = min(z[i - l + 1] , r - i + 1);
		else z[i] = 0;
		while(s[z[i] + 1] == s[i + z[i]]) z[i] += 1;
		if(i + z[i] - 1 > r) l = i , r = i + z[i] - 1;
	}
}

signed main(){

	cin >> n >> m >> s;
	
	ans = s;s += s;n += n;
	get_z(s);
	s = ' ' + s;
	
	for(int i = 1 ; i <= n / 2 ; i ++) {
		int pos = z[i + 1];
		if(pos < i && s[pos + 1] < s[i + pos + 1]) {
			id = i; break;
		}
	}
	
	if(id != -1) ans = s.substr(1 , id);
	
	int cnt = (m - 1) / ((int)ans.size()) + 1;
	
	for(int i = 1 ; i <= cnt ; i ++) {
		res += ans;
	}
	
	res.resize(m);
	
	cout << res << "\n";

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

/*
10 11
yyynhtexfv
8 17
dbcadabc
*/

CF 165 C. Another Problem on Strings

Problem - C - Codeforces

大意:给出一个二进制字符串 s , 和一个数 k , 求 s 中包含 k 个 1 的子串个数。

思路:考虑连续 k 个 1 的贡献是最左侧 1 及 1 左侧连续 0的长度 和 右侧 1 及 1 右侧连续 0的长度的乘积 , 处理出每个 1 左右侧的连续 0 的长度 , 枚举区间贡献即可。

#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 = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;

int k , n;
string s;
int pre[N] , nex[N] , res;

signed main(){

	IOS
	cin >> k >> s;
	n = s.size();
	s = '?' + s;
	vector<int>ve;
	int cnt = 0;
	
	for(int i = 1 ; i <= n ; i ++) {
		if(s[i] == '1') {
			ve.push_back(cnt);
			ve.push_back(-1);
			cnt = 0;
		} else {
			cnt += 1;
			if(i == n) ve.push_back(cnt);
		}
	}
	if(ve.back() == -1) ve.push_back(0);
	
	n = ve.size();
	cnt = 0;
	
	if(k == 0) {
		for(int i = 0 ; i < n ; i ++) {
			if(ve[i] != -1) res += (ve[i] + 1) * ve[i] / 2;
		}
	} else {
		for(int i = 0 ; i < n ; i ++) {
			if(ve[i] == -1) {
				nex[++cnt] = ve[i + 1];
				pre[cnt]   = ve[i - 1];
			}
		}	
		if(cnt < k) {
			res = 0;
		} else {
			for(int i = 1 ; i + k - 1 <= cnt ; i ++) {
				res += (pre[i] + 1) * (nex[i + k - 1] + 1);
			}
		}
	}
	
	cout << res << "\n";
	

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

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