A. Prefix and Suffix Array
题意:给定一个字符串的长度n,并按任意顺序给出n * 2 - 2个前缀和后缀子串,判断该串是不是pralindrome字符串
思路:找到两个n-1的字符串,然后判断出两个子串哪个是前缀,哪个是后缀,将子串拼接成原本的字符串s,再判断是不是回文串。
// Created on ?.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
typedef vector<int> vi;
typedef vector<vector<int>> vvi;
typedef long long int ll;
bool if_parlindrome(string& s){
int l = 0,r = s.size() -1;
while (l < r){
if (s[l] != s[r]) return false;
l += 1, r -= 1;
}
return true;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int N;
cin >> N;
while (N--){
int n;
cin >> n;
int t = 2 * n - 2;
string s1 = "",s2 = "", tmp;
while (t--){
cin >> tmp;
if (tmp.size() != n - 1) continue;
s2 = s1, s1 = tmp;
}
//cout << s1 << " " << s2 << endl;
if (s1.substr(1, s1.size() - 1) == s2.substr(0, s2.size() - 1)) s1 += s2[n - 2];
else s1 = s2[0] + s1;
//cout << s1 << endl;
cout << (if_parlindrome(s1) ? "YES" : "NO") << endl;
}
}
总结:一开始在区分前后缀子串时用的方法是看s1的子串的第二个字符是不是跟s2的第一个字符相等来进行区分,测试示例可以跑过去,但是后续会出错,后来改变了判定字符串的方式才跑过去。
B. Not Dividing
题意:给定一个数组,可以对这个数组的任意元素执行+1操作,且操作总数不超过2n,n为数组长度,且操作后的数组需后面的数MOD前面的数不为0。
思路:假设a与b相邻,b在a后面,那么若要b % a != 0,如果a为1,则b无论如何修改都不行,所以在扫描数组的时候先将所有为1的数值加1改为2。其次如果b<a,那么b % a 必然不为0,所以秩序要将数组从左往右遍历一次,将不满足条件的b往上累加直到满足条件即可。
// Created on ?.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
typedef vector<int> vi;
typedef vector<vector<int>> vvi;
typedef long long int ll;
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int N;
cin >> N;
while (N--){
int n;
cin >> n;
vi a(n);
for (int i = 0; i < n; ++i){cin >> a[i]; if (a[i] == 1) a[i] += 1;}
for (int i = 1; i < n; ++i){
while (a[i] % a[i - 1] == 0) {a[i] += 1; }
}
for (int i = 0; i < n; ++i){cout << a[i] << " \n"[i + 1 == n];}
}
}
总结:一开始考虑的是如果相邻的两个数存在不同的质因子,那么就必然不能整除,于是打算在输入数据时对每个数进行质因子扫描,但是想了想好像有点麻烦,需要写很多规则出来,就直接brute force了,没想到速度还挺快。
C. Scoring Subsequences
题意:
给定一个长度为n的数组a,规定a[i]的score为max(a[i] * a[i - 1] * ..... a[j]/ 1 * 2 *...(i - j + 1)),
0 <=j <= i <= n,对于每个a[i]的score m,找到a[0] ~ a[i]中score为m的最长子串的长度。
思路:score的确定是以i为边界,不断的向左移动,找一个左边界,这个最大的左边界就是最大子串的长度。因为score的公式是a[i]的元素不断的往左乘,然后除以元素数的阶乘,也就是说在往左寻找的过程中,只要元素的数值大于该位置阶乘要乘的数字的值,那么这个位置就可以保留下来继续向左延伸,一直到a[j] < k (2 <= k <= i + 2),先上暴力破解的代码。
// Created on ?.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
typedef vector<int> vi;
typedef vector<vector<int>> vvi;
typedef long long int ll;
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int N;
cin >> N;
while (N--){
int n;
cin >> n;
vi a(n + 1, 0);
for (int i = 1; i <= n; ++i){
cin >> a[i];
int k = 2, j = i - 1;
while (j > 0 && k <= a[j]){
j -= 1;
k += 1;
}
cout << i - j << " \n"[i == n];
}
}
}// 1 1 2 2 3 3 4 4
不出意外,Brute force果然超时,考虑到输入数据为10的五次方级别,这里的查找方式要更改一下,于是想到了二分查找。
对于二分查找, 可以将i-1在的位置设为左边界,然后0为右边界,每次可以求出区间的中间值m。这里设一个dis = i - m + 1,这个值就是i向左延申的左边界,用dis去和a[m]做比较,如果dis <= a[m],那么可以继续向左查找。
上代码
// Created on ?.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
typedef vector<int> vi;
typedef vector<vector<int>> vvi;
typedef long long int ll;
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int N;
cin >> N;
while (N--){
int n;
cin >> n;
vi a(n + 1, 0);
for (int i = 1; i <= n; ++i){
cin >> a[i];
int l = i - 1, r = 1;
while (r <= l){
int m = (l + r) >> 1, dis = i - m + 1;
if (a[m] < dis){r = m + 1;}
else if (a[m] >= dis){l = m - 1;}
}
cout << i - l << " \n"[i == n];
}
}
}// 1 1 2 2 3 3 4 4
总结:这个题的难点关键在于理解题意,因为题目一开始好像说的不清楚,或者是我英文水平低,没理解题意。理解了题意以后还要知道最大score是如何计算出来的,因为一开始写的时候是用了动态规划,让f[i] = max (f[i - 1] * idx! , a[i]),这样得到的并不是最大score....之后确定了如何得到最大score以后就要在查找上面下功夫了,这个题的查找不是很难,关键在于下标的确定。
D. Counting Factorizations
一开始以为是简单的爬楼梯问题,但是怎么写都感觉有点问题,然后发现p是一个递增序列,暂时有点懵,先跳过了。