everyday two problems / 3.1
题意:
给定由小写字母组成的长度为 n 的字符串
将其所有 n * (n + 1) / 2 个子串按字典序排序
输出第 k 个子串
k > (n * (n + 1) / 2) 就输出"No such line."
1 <= n, k <= 1e5
解法:
解法很多(我不会写自动机啊
我用的解法应该算是比较简单
一位一位地去计数来确定当前位的字母
比如一开始O(n)扫描出以a开头的子串有3个
以b开头的子串有4个,而我要输出排名第5的子串
所以这一位一定是'b',后面每一位字母的确定同理
当前位是空格的时候标志着结果输出结束
代码比较简单,可以参考
但是我们要说明一下效率
一开始脑残了误以为是O(26n)
但是其实每次while中运行次数与之前已经输出的子串在原字符串中出现的次数有关
那么我们可以考虑最坏情况,10w个'a'
这时候如果要求输出排名最靠后的子串的话效率已经O(n^2)
还好我们题目有限制 k <= 1e5
所以这种做法的最坏效率其实比较玄学(但是过了就好了233
1 #include <cstdio> 2 #include <vector> 3 #include <cstring> 4 5 using namespace std; 6 7 typedef long long ll; 8 9 vector <int> p[27], pp; 10 11 char str[100010]; 12 13 ll cnt[27]; 14 15 int n, m; 16 17 int main() { 18 scanf("%s %d", str, &m); 19 n = strlen(str); 20 if(1ll * n * (n + 1) / 2 < m) { 21 puts("No such line."); 22 return 0; 23 } 24 for(int j, i = 0;i < n;i ++) { 25 j = str[i] - 96; 26 cnt[j] += n - i; 27 p[j].push_back(i); 28 } 29 while(1) { 30 int pos; 31 for(int i = 0;i <= 26;i ++) { 32 if(cnt[i] >= m) { 33 pos = i; 34 if(!pos) return 0; 35 putchar(96 + i); 36 break; 37 } 38 else m -= cnt[i]; 39 } 40 for(int i = 1;i <= 26;i ++) { 41 cnt[i] = 0; 42 if(i != pos) p[i].clear(); 43 } 44 cnt[0] = p[pos].size(); 45 for(int k, j, i = 0;i < p[pos].size();i ++) { 46 j = p[pos][i] + 1; 47 if(j >= n) continue; 48 k = str[j] - 96; 49 cnt[k] += n - j; 50 if(k == pos) pp.push_back(j); 51 else p[k].push_back(j); 52 } 53 p[pos].clear(); 54 for(int i = 0;i < pp.size();i ++) 55 p[pos].push_back(pp[i]); 56 pp.clear(); 57 } 58 }
题意:
给定数组a[2000],-1e9 <= a[i] <= 1e9
最多允许修改 k(k <= n) 个元素
求 max(abs(a[i] - a[i + 1])) 的最小值
解法:
这题是看别人题解做的
猜测正解效率可能n^2
但并没有什么策略能直接由条件得到答案
满足单调,考虑二分,可能效率n^2logn,可以接受
然后这个judge函数就比较神了
考虑dp,f[i]表示先只考虑前 i 个且第 i 个不动的情况下
最少修改多少个能够让前 i 个的 max(abs(a[i] - a[i + 1])) <= mid
转移方程 f[i] = min(f[i], f[j] + i - j - 1)
转移条件是abs(f[i] - f[j]) <= (i - j) * mid
即,保证i j 都不变,(i, j)都改变
要能够使[i, j]任意相邻两个之差 <= mid才能转移
上面我们提到f[i]的意义,只考虑了前 i 个
但我们考虑最优解一定有最后连续 t 个都是要被修改的(0 <= t < n)
所以我们计算完所有 f[i] 之后
在从 1 到 n 扫一遍 f[i] + n - i
是一定能够包含最优解的
当然f[i] + n - i <= k就可以返回true了
1 #include <cstdio> 2 3 typedef long long ll; 4 5 int n, k, a[2010], f[2010]; 6 7 int min(int x, int y) { 8 return x < y ? x : y; 9 } 10 11 int dis(int x, int y) { 12 if(x < y) return y - x; 13 return x - y; 14 } 15 16 bool judge(ll d) { 17 f[1] = 0; 18 for(int i = 2;i <= n;i ++) { 19 f[i] = 66666; 20 for(int j = i- 1;j;j --) 21 if(dis(a[i], a[j]) <= d * (i - j)) 22 f[i] = min(f[i], f[j] + i - j - 1); 23 if(i <= k + 1) f[i] = min(f[i], i - 1); 24 } 25 for(int i = 1;i <= n;i ++) 26 if(f[i] + n - i <= k) 27 return 1; 28 return 0; 29 } 30 31 int main() { 32 scanf("%d %d", &n, &k); 33 for(int i = 1;i <= n;i ++) 34 scanf("%d", &a[i]); 35 ll l = 0, r = 2e9, mid; 36 while(l <= r) { 37 mid = (l + r) >> 1; 38 if(judge(mid)) r = mid - 1; 39 else l = mid + 1; 40 } 41 printf("%I64d", l); 42 return 0; 43 }