for (int i = 0, j = 0; i < n; i ++ ) {
while (j < i && check(i, j)) j ++;
/*具体问题逻辑*/
}
常见双指针问题:
#include
using namespace std;
const int MAX = 100010;
int n;
int a[MAX], mark[MAX];
bool check(int low, int high) {
for (int i = low + 1; i <= high; ++i) {
for (int j = low; j < i; ++j) {
if (a[i] == a[j]) return false;
}
}
return true;
}
int main() {
int res = 0;
cin >> n;
for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
for (int i = 0; i < n; ++i) {
for (int j = 0; j <= i; ++j) {
if (check(j, i)) res = max(res, i - j + 1);
}
}
cout << res << endl;
return 0;
}
仔细考虑暴力法就会发现,暴力法在解题时有很多地方是重复计算了:
#include
using namespace std;
const int MAX = 100010;
int n;
int a[MAX], mark[MAX];
bool check(int low, int high) {
for (int i = low + 1; i <= high; ++i) {
for (int j = low; j < i && mark[a[i]] > 0; ++j) {
if (a[i] == a[j]) return false;
}
}
return true;
}
int main() {
int res = 0;
cin >> n;
for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
for (int i = 0; i < n; ++i) {
while (j <= i) {
if (!check(j, i)) j++;
else {
res = max(res, i - j + 1);
break;
}
}
}
cout << res << endl;
return 0;
}
由于check函数写的不好循环太多,直接是暴力计算找重复数字的,显然不好。
所以引出一个新的check方法:对于寻找是否有重复数字,一般用hash,没人用暴力。
i
找到j
使得[j, i]
维护的是以a[i]
结尾的最长连续不重复子序列,长度为i-j+1
, 将最大的长度res更新。[j, i - 1]
是前一步得到的最长连续不重复子序列,所以如果[j, i]
中有重复元素,一定是a[i]重复,因此右移j
直到a[i]
不重复为止(由于[j, i - 1]
已经是前一步的最优解,此时j
只可能右移以剔除重复元素a[i]
,不可能左移增加元素,因此,j具有单调性、本题可用双指针降低复杂度)。a[j ~ i]
中各元素出现次数,遍历过程中对于每一个i
有四步操作:读入元素a[i] -> 将a[i]出现次数count[a[i]]
加1 -> 若a[i]
重复则右移j(count[a[j]]
要减1) -> 确定j
及更新当前长度i - j + 1
给res。#include
using namespace std;
const int MAX = 100010;
int n;
int a[MAX], count[MAX];
int main() {
int res = 0;
cin >> n;
for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
for (int i = 0, j = 0; i < n; ++i) {
count[a[i]]++;
while (count[a[i]] > 1) {//某个数字的数量大于1该数字重复, 将j指针移动到与i指针相同的位置
count[a[j]]--;
j++;
}
res = max(res, i - j + 1);
}
cout << res << endl;
return 0;
}
参考:https://www.acwing.com/solution/content/23474/
双指针算法就是对暴力法进行优化,如果有单调性,则可以利用单调性将时间复杂度降低一位:
#include
using namespace std;
const int MAX = 100010;
int n, m, x;
int A[MAX], B[MAX];
int main() {
scanf("%d %d %d", &n, &m, &x);
for (int i = 0; i < n; ++i) scanf("%d", &A[i]);
for (int i = 0; i < m; ++i) scanf("%d", &B[i]);
for (int i = 0, j = m - 1; i < n; ++i) {
//int j = m - 1;不可写在for循环中
while (A[i] + B[j] > x && j >= 0) j--;
if (A[i] + B[j] == x) {
printf("%d %d\n", i, j);
break;
}
}
return 0;
}
#include
using namespace std;
const int N = 100010;
int n, m;
int arr1[N], arr2[N];
int main() {
scanf("%d %d", &n, &m);
for (int i = 0; i < n; ++i) scanf("%d", &arr1[i]);
for (int i = 0; i < m; ++i) scanf("%d", &arr2[i]);
int i = 0, j = 0;
while (i < n && j < m) {
if (arr1[i] == arr2[j]) i++;
j++;
}
if (i == n) puts("Yes");
else puts("No");
return 0;
}