Codeforces Round #506 (Div. 3)
题目链接:https://codeforces.com/contest/1029
A题题解
概述
给定子字符串,构造包含k个该子字符串的最短字符串。
思路
作为div 3的第一道题自然相当随意,数据量极小,O(k*n^3)也能过的样子,可以说有思路就没有问题,事实上我也看到有大佬迭代遍历AC这道题。
不过当我初步推理这道题时,我马上联想到KMP算法。直觉告诉我这就是这道题的最优解法,而且可以利用现成代码。接下来我就讲一讲这道题关于KMP的解法~
还不理解KMP算法的话...出门左转见度娘~
首先想到,最傻的方法就是把k个t串起来,必然保证有k个t;
如何使字符串长度更短?
假设有某种办法,那么随着总长度缩短,必然造成相邻的子字符串相互重叠,但不会完全重合或存在包含关系。换句话说,我们只需要使子字符串之间重合部分最长,就得到了最短的目标字符串。
而每个子字符串是相等的,也就是求子字符串自己头和尾的最长重合部分。
例如子字符串t="abcab",“ab”就是t的最长重合部分。比如k=2时,s="abcabcab"。
讲到这里熟悉KMP的朋友们就会发现,这正是KMP实现的核心思想,KMP初始化fail/next数组就保存着这样的信息。于是我们借用KMP的初始化函数,顿时这道题只剩下简单的处理。
注意重合部分不能是子字符串本身,例如t="aaa"时最多只能重合"aa"。
#include<bitsstdc++.h> using namespace std; string r,s; int fail[100]{0}; void make_fail(){ for(int i=1,j=0;s[i];i++){ while(j && s[i]!=s[j]) j=fail[j-1]; if(s[i]==s[j])fail[i]=++j; else fail[i]=0; } } int main(){ int n,k; cin>>n>>k; cin>>s; make_fail(); int p=n-fail[n-1]; if(p==0) p=1; for(int i=0;i<k-1;i++) r.append(s.substr(0,p)); r.append(s); cout<<r; return 0; }
运用substr来做
#include<stdio.h> #include<iostream> #include<bits/stdc++.h> using namespace std; int main(){ int n,k; cin >> n >> k; string s; cin >> s; int temp; for(int i = 0; i < n ; i++) if(s.substr(0,i) == s.substr(n-i,i)) temp = i; for(int i = 0 ; i < k-1 ; i++) cout << s.substr(0,n-temp); cout << s << endl; return 0; }
B题题解
https://blog.csdn.net/intmainhhh/article/details/82286299
#include<stdio.h> #include<iostream> #include<bits/stdc++.h> using namespace std; int a[200000+10]; int main(){ int n; cin >> n; int x = ~1+1; //cout << x << endl; int sum = 1; for(int i = 0; i < n; i++){ cin >> a[i]; if(i > 0){ if(a[i] <= a[i-1] * 2) sum++; else { x = max(sum,x); sum = 1; } } // cout <<sum << endl; } //if(x==0) //cout << "1" << endl; //else x = max(sum,x); cout << x << endl; return 0;
C题题解
multiset弄一下;
https://www.cnblogs.com/Agnel-Cynthia/p/10604317.html
#include<bits/stdc++.h> using namespace std; int l[300005], r[300005]; multiset<int> a, b; int main(){ int n; cin >> n; for(int i = 0 ; i < n ; i++){ cin >> l[i]; cin >> r[i]; a.insert(l[i]); b.insert(r[i]); } int ans = ~1 + 1 + 1; for(int i = 0 ; i < n; i ++){ a.erase(a.find(l[i])); b.erase(b.find(r[i])); ans = max(ans,*b.begin() - *a.rbegin()); a.insert(l[i]); b.insert(r[i]); } cout << ans << endl; return 0; }
D题待补
E题待补
F题题解
数学题,就是已知两个颜色矩形的面积,要求最小周长,且满足其中一种颜色的为矩形
思路:面积相同,长越大周长越小。暴力遍历。
优化:
1.将宽从大到小遍历,如果满足,必然最小,直接输出
2.判断小矩形时,从大到小判断,减少复杂度(
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll a ,b ,c ,d; const ll inff = 0x3f3f3f3f3f3f3f3fLL; //18 bool check(ll x , ll y){ for(ll i = x ; i >= 1 ; i-- ){ if(a % i == 0 && a / i <= y) return 1; if(b % i == 0 && b / i <= y) return 1; } return false ; } int main(){ //ll a, b; cin >> a >> b; c = a + b; ll ans = inff; //从大往小枚举,即宽最大时,周长最小 for(ll i = 1ll; i * i <= c; i++){ if(c % i == 0 ){ d = c / i; if(check(i,d)){ ans = min(ans,2ll * (d + i)); //不优化反而更快 // cout << ans << endl; // return 0; } } } cout << ans << endl; }