一、题意
输入n、m、k,你可以根据m得到一个长度为n的序列a[maxn](即a[maxn]已知)。
现在要你在a[maxn]中找到一个包含 1~k 这k个整数的最短连续子序列。
输出符合要求的最短连续子序列的长度。无解时输出无解。
二、解析
看到连续子序列,马上就觉得可以用滑动窗口或者说双指针的方法来做。
同时还需要用一个 cnt[maxk] 来存放当前“窗口”中 1 ~ k 的数字分别有多少个。通过 j 递增来滑动窗口,同时当 i 所指向的值不属于 1 ~ k 或者它所指向的值在窗口中已经有多个时,可以将其弹出窗口(以使得窗口尽可能小)。然后通过对cnt数组中1~k的元素是否有值来判断当前窗口是否ok。
用一个ans值记录一个ok的窗口的最短长度即可。
三、代码
#include <iostream> #include <cmath> using namespace std; const int maxn = 1000000 + 5, maxk = 100 + 5; int kase, n, m, k, a[maxn], cnt[maxk]; bool isOK() { for(int i = 1; i <= k; i ++) if(cnt[i] == 0) return 0; return 1; } int main() { cin >> kase; a[0] = 1, a[1] = 2, a[2] = 3; for(int K = 1; K <= kase; K ++) { cin >> n >> m >> k; fill(cnt, cnt + k + 1, 0); for(int i = 3; i < n; i ++) a[i] = (a[i - 1] + a[i - 2] + a[i - 3]) % m + 1; bool ok = 0; int ans = n; for(int i = 0, j = 0; j < n; j ++) if(a[j] <= k) { cnt[a[j]] ++; if(!ok && isOK()) ok = 1; while(a[i] > k || cnt[a[i]] > 1) { // 注意不在1~k范围内的值也可以直接出列 if(a[i] <= k) cnt[a[i]] --; i ++; } if(ok) ans = min(ans, j - i + 1); } if(!ok) cout << "Case " << K << ": sequence nai" << endl; else cout << "Case " << K << ": " << ans << endl; } }
四、归纳
- 双指针问题注意 i++ 的条件,不要遗漏。