双指针题目选做
NC18386 字符串
这道题几乎就是双指针的模板题了。
每次移动一下左端点,然后移动右端点知道满足条件
#include<bits/stdc++.h>
using namespace std;
int v[300] = {};
int32_t main() {
string s;
cin >> s;
int l = 0 , r = -1 , len = s.size() , ans = 1e9 , k = 0;
while( l < len ){
while( r < len - 1 && k < 26 ) { // 右指针右移
r++, v[s[r]]++;
if (v[s[r]] == 1) k++;
}
if( k == 26 ) ans = min( ans , r - l + 1 ); // 满足条件
if( v[s[l]] == 1 ) k --; // 左指针右移
v[s[l]] -- , l ++;
}
cout << ans << endl;
return 0;
}
NC207040 丢手绢
这道题也可以用双指针来做。但是值得注意的是因为圆的性质,所以圆上两点之间的距离一定是小于等于半径的。所以如果右指针移动后距离大于半径了,右指针的移动就可以结束了。
#include<bits/stdc++.h>
using namespace std;
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
const int N = 1e5+5;
int n , a[N] , ans , sum , cnt ;
int32_t main() {
n = read();
for( int i = 0 ; i < n ; i ++ )
a[i] = read() , sum += a[i];
for( int i = 0 , j = -1 ; i < n ; i ++ )
{
while( cnt * 2 < sum ){
j = ( j +1 ) % n , cnt += a[j];
ans = max( ans , min( cnt , sum - cnt ) );
}
cnt -= a[i];
}
cout << ans << endl;
return 0;
}
luogu P1102 A-B 数对
这道题主要运用了双指针的方法来写,但是需要运用一些 hash 的思想
在 map 中枚举两个指针,一直移动右指针直到r-l>=c
然后判断是否相等,相等就更新答案
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
const int N = 1e5+5;
int n , c , cnt;
map< int , int > a;
int32_t main() {
n = read() , c = read();
for( int i = 1 , x ; i <= n ; i ++ )
x = read() , a[x] ++;
for( auto i = a.begin() , j = i ; i != a.end() && j!= a.end() ; i ++ ){
while( j != a.end() && j->first - i->first < c ) j ++ ;
if( j->first - i->first == c )
cnt += i->second * j->second;
}
cout << cnt << endl;
return 0;
}
luogu P1638 逛画展
比较裸的双指针问题,每次维护一下区间内画的种数就好了
#include<bits/stdc++.h>
using namespace std;
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
const int N = 1e6+5 , M = 2e3+5;
int n , m , a[N] , cnt , ansl , ansr , ans = 1e9;
int vis[N];
int32_t main() {
n = read() , m = read();
for( int i = 1 ; i <= n ; i ++ )
a[i] = read();
for( int l = 1 , r = 0 ; l <= n ; l ++ ){
while( r < n && cnt < m )
r ++ , vis[ a[r] ] ++ , cnt += (vis[ a[r] ] == 1 );
if( cnt == m && ans > r - l + 1 )
ans = r - l + 1 , ansl = l , ansr = r;
cnt -= ( vis[ a[l ] ] == 1 ) , vis[ a[l] ] --;
}
cout << ansl << " " << ansr << endl;
return 0;
}
luguoP1381 单词背诵
这道题首先要判断那些单词是不用背的,需要背的单词有多少个,然后用双指针维护出最短的区间。
注意如果需要背的有0 个,那么区间也一定是0
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5 , M = 1005;
map< string , int > hs;
int n , m , a[N] , ans = 1e9;
int vis[N];
string cur;
int32_t main() {
cin >> n;
for( int i = 1 ; i <= n ; i ++ )
{
cin >> cur;
hs[cur] = i;
}
cin >> m;
for( int i = 1 ; i <= m ; i ++ )
{
cin >> cur;
if( hs.find(cur) == hs.end() ) a[i] = -1;
else a[i] = hs[ cur ] , vis[ a[i] ] = 1;
}
int t = 0;
for( int i = 1 ; i <= n ; i ++ )
t += vis[i] , vis[i] = 0;
cout << t << endl;
if( t == 0 )
printf("0\n") , exit(0);
for( int l = 1 , r = 0 , cnt = 0 ; l <= m ; l ++ ){
while( r < m && cnt < t ){
r ++ ;
if( a[r] == -1 ) continue;
vis[ a[r] ] ++ , cnt += ( vis[a[r]] == 1 );
}
if( cnt == t && r - l + 1 < ans )
ans = r - l + 1 ;
if( a[l] != -1 ) cnt -= ( vis[ a[l] ] == 1 ) , vis[ a[l] ] --;
}
cout << ( ans == 1e9 ? 0 : ans ) << endl;
return 0;
}