暴力:遍历所有子段
时间复杂度为O(n^2)
for(int i=0;i<n;i++){
for(int j=0;j<=i;j++){
}
}
优化:
i,j总共走2n,则时间复杂度为O(n)
法一:
j:当i移动到一个位置时,j往左最远能到什么位置,使此时的i,j为满足的子段
j指向该满足题意子段的起点,i指向该满足题意子段的终点
第一层写for,第二层写while(j<终点&&check(i,j))j++;
for(int i=0;j=0;i<n;i++){
while(j<=i&&check(j,i)) j++;
//每道题的具体逻辑
}
适用于后面的指针i从1到n遍历,前面的指针j随之变动,维持区间满足要求
当然,将j<=i改为j<n也可以后面指针j移动维持区间满足要求
(其中一个指针不停地严格地一个一个往后移,另一个指针来维持区间满足要求)
while循环用以维持区间满足要求
注意边界,可能后面的指针出边界了,但是仍将其作为答案
法二:利用i,j两个指针,i,j都从头开始走,然后j先开始移动,使得[i,j]为满足题意的区间子段,然后j往后一格,i移动维持[i,j]仍满足题意
while(i<=j&&j<=n){
if(){
j++;
}
else{
i++;
}
}
既可以后面指针一个一个移动,前面指针维持区间满足要求,也可以前面指针一个一个移动,后面指针满足区间要求
利用if和else 使得两个指针的移动每次都二选一,且每次只移动一格,只要考虑什么时候前面的指针移动,什么时候后面的指针移动(全部都写在if和else里面)
注意,因为每次只移动一格,因而不是每时每刻区间都是满足的,所以,提取答案时要有条件,一是保证区间满足题意,二是边界,可能后面的指针出边界了,但是仍将其作为答案
例1:
输入:
abc def ghi
输出:
abc
def
ghi
法一:
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
char str[1000];
gets(str);
int n=strlen(str);
for(int i=0;i<n;i++){
int j=i;
while(j<n&&str[j]!=' ') j++;
for(int k=i;k<j;k++) cout<<str[k];
cout<<endl;
i=j;
}
return 0;
}
法二:
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
char str[1000];
gets(str);
int n=strlen(str);
str[n]=' ';
int i=0,j=0;
while(i<=j&&j<=n){
if(str[j]!=' '){
j++;
}
else{
for(int k=i;k<=j;k++) cout<<str[k];
cout<<endl;
i=j+1;
j=i;
}
}
return 0;
}
例2:
给定一个长度为n的整数序列,请找出最长的不包含重复数字的连续子序列,输出它的长度
s数组用于计数,桶的计数
当i向右移动的时候,那么[j,i]区间子段就加了一个数,由于加了一个数,那么该子段就可能会有重复的数,而该重复的数只可能由加入的这个数产生,因此如果s[a[i]]>1,则说明重复,于是j移动,使得[i,j]区间子段维持没有重复数字
s[a[j]]--表示[i,j]区间子段减去一个数,桶a[j]计数相应减去一个
s[a[i]]++表示[i,j]区间子段加上一个数,桶a[i]计数相应加上一个
法一:
#include<iostream>
using namespace std;
const int N=100010;
int a[N],s[N];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
int res=0;
for(int i=0,j=0;i<n;i++){
s[a[i]]++;
while(j<=i&&s[a[i]]>1){
s[a[j]]--;
j++;
}
res=max(res,i-j+1);
}
cout<<res<<endl;
return 0;
}
法二:
#include<iostream>
using namespace std;
const int N=1e6+5;
int a[N],s[N];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int res=0;
int i=1,j=1;
s[a[1]]++;
while(i<=j&&j<=n){
if(s[a[j]]>1){
s[a[i]]--;
i++;
}
else{
j++;
s[a[j]]++;
}
if(s[a[j]]==1&&j<=n) res=max(res,j-i+1);
}
cout<<res<<endl;
return 0;
}
例3:
博览馆正在展出由世上最佳的 m 位画家所画的图画。
游客在购买门票时必须说明两个数字a和b,代表他要看展览中的第a幅至第b幅画(包含 a,b)之间的所有图画,而门票的价钱就是一张图画一元。
W不仅仅是一个acmer,还是一个艺术爱好者。
所以W希望入场后可以看到所有名师的图画。
于是他让可怜的工具人Tree去帮助他买到最便宜的票。
你能帮忙Tree算出花费最少的票(a,b)吗
如果有多种最便宜的票请输出a最小的一组
前面的指针一个一个移动,后面的指针维持区间满足要求
法一:
#include<iostream>
using namespace std;
const int N=1e6+5;
int a[N],s[N];
int main()
{
int n,m,t,cnt=1;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int res=1e6+5,x=0,y=0;
s[a[1]]++;
for(int i=1,j=1;i<=n;i++){
while(j<n&&cnt!=m){
j++;
s[a[j]]++;
if(s[a[j]]==1) cnt++;
}
if(cnt==m){
if(res>j-i+1){
res=j-i+1;
x=i;
y=j;
}
s[a[i]]--;
if(s[a[i]]==0) cnt--;
}
}
cout<<x<<' '<<y<<endl;
return 0;
}
法二:
#include<iostream>
using namespace std;
const int N=1e6+5;
int a[N],s[N];
int main()
{
int n,m,t,cnt=1;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int res=1e6+5,x=0,y=0;
int i=1,j=1;
s[a[1]]++;
while(i<=j&&j<=n){
if(cnt!=m){
j++;
s[a[j]]++;
if(s[a[j]]==1) cnt++;
}
else{
if(res>j-i+1){
res=j-i+1;
x=i;
y=j;
}
s[a[i]]--;
if(s[a[i]]==0) cnt--;
i++;
}
}
cout<<x<<' '<<y<<endl;
return 0;
}