目录
子串简写
程序猿圈子里正在流行一种很新的简写方法:对于一个字符串,只保留首尾字符,将首尾字符之间的所有字符用这部分的长度代替。例如 internation-alization 简写成 i18n,Kubernetes (注意连字符不是字符串的一部分)简写成 K8s, Lanqiao 简写成 L5o 等。
在本题中,我们规定长度大于等于 K 的字符串都可以采用这种简写方法(长度小于 K 的字符串不配使用这种简写)。
给定一个字符串 S 和两个字符 c1 和 c2,请你计算 S 有多少个以 c1 开头c2 结尾的子串可以采用这种简写?
输入格式
第一行包含一个整数 K。
第二行包含一个字符串 S 和两个字符 c1 和 c2。
输出格式
一个整数代表答案。
样例输入
4
abababdb a b
样例输出
6
提示
符合条件的子串如下所示,中括号内是该子串:
[abab]abdb
[ababab]db
[abababdb]
ab[abab]db
ab[ababdb]
abab[abdb]
对于 20% 的数据,2 ≤ K ≤ |S | ≤ 10000。
对于 100% 的数据,2 ≤ K ≤ |S | ≤ 5 × 105。S 只包含小写字母。c1 和 c2 都是小写字母。
|S | 代表字符串 S 的长度。
思路:把字符串中等于a的下标都存起来,把等于b的下标也都存起来。然后判断两者下标之间有无大于等于k,直接加上(b的个数-当前b下标(因为cnt2等于b的个数加1,所以直接减就好,不用在加1)
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=5e5+10;
int l[N],r[N];
int main()
{
int k;cin>>k;
string s;cin>>s;
char a,b;cin>>a>>b;
int cnt1=1,cnt2=1;
for(int i=0;i<s.size();i++)
{
if(s[i]==a)
{
l[cnt1++]=i;
}
if(s[i]==b)
{
r[cnt2++]=i;
}
}
long long ans=0;
int idx=1;
for(int i=1;i<cnt1;i++)
{
for(int j=idx;j<cnt2;j++)
{
if(r[j]-l[i]<k-1) continue;
else
{
ans+=(cnt2-j);
idx=j;
break;
}
}
}
cout<<ans<<endl;
return 0;
}
双指针
1、左右指针:两个指针,相向而走,中间相遇
2、快慢指针:两个指针,又快又慢,同向而行
3、灵活运用:两个指针,灵活运用,伺机而动
左右指针
盛最多水的容器
class Solution {
public:
int maxArea(vector<int>& height) {
if(height.size()<=1) return 0;
int sum=0;
int l=0,r=height.size()-1;
while(l<r)
{
int num=min(height[l],height[r]);
sum=max(num*(r-l),sum);
sum=max(sum,num);
if(height[l]<height[r]) l++;
else r--;
}
return sum;
}
};
小结
左右双指针法的实质是合理利用题目的规则,减少遍历的次数,从而降低时间复杂度。可以从暴力出发,想办法利用题目规则,一步一步进行优化。
最长连续不重复子序列
给定一个长度为 n 的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。
输入格式
第一行包含整数 n。
第二行包含 n 个整数(均在 0∼10^5 范围内),表示整数序列。
输出格式
共一行,包含一个整数,表示最长的不包含重复的数的连续区间的长度。
数据范围
1≤n≤10^5
输入样例:
5
1 2 2 3 5
输出样例:
3
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef pair<int,int>PII;
const int N=5e5+10;
int a[N],s[N];
int main()
{
int n;cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
int sum=0;
//一个指针一直往前走 另一个指针停留 直到遇到重复的时候 无法组成 这时另一个指针
//向前走 直到 没有重复的 在记录-->O(n)
for(int i=1,j=1;j<=n;j++)
{
s[a[j]]++;
while(s[a[j]]>1)
{
s[a[i]]--;
i++;
}
sum=max(sum,j-i+1);
}
cout<<sum<<endl;
return 0;
}
数组元素的目标和
给定两个升序排序的有序数组 A和 B,以及一个目标值 x。
数组下标从 0 开始。
请你求出满足 A[i]+B[j]=x的数对 (i,j)。
数据保证有唯一解。
输入格式
第一行包含三个整数 n,m,x,分别表示 A 的长度,B 的长度以及目标值 x。
第二行包含 n 个整数,表示数组 A。
第三行包含 m 个整数,表示数组 B。
输出格式
共一行,包含两个整数 i 和 j。
数据范围
数组长度不超过 10^5。
同一数组内元素各不相同。
1≤数组元素≤10^9
输入样例:
4 5 6
1 2 4 7
3 4 6 8 9
输出样例:
1 1
#include<iostream>
using namespace std;
const int N=100010;
int a[N],b[N],s[N];
int main()
{
int n,m,x;cin>>n>>m>>x;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<m;i++) cin>>b[i];
for(int i=0,j=m-1;i<n;i++)
{
while(j>=0&&a[i]+b[j]>x) j--;
if(a[i]+b[j]==x)
{
cout<<i<<" "<<j;
break;
}
}
return 0;
}
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll n,m,x;
ll a[N],b[N];
int main()
{
cin>>n>>m>>x;
for(int i=0;i<n;i++) cin>>a[i];
for(int j=0;j<m;j++) cin>>b[j];
for(int i=0,j=m-1;i<n,j>0;)
{
while(a[i]+b[j]>x) j--;
while(a[i]+b[j]<x) i++;
if(a[i]+b[j]==x)
{
cout<<i<<" "<<j<<endl;
break;
}
}
return 0;
}
更小的数
天梯赛中的最长对称子串考察的与其一样
小蓝有一个长度均为 n 且仅由数字字符 0 ∼ 9 组成的字符串,下标从 0 到 n − 1,你可以将其视作是一个具有 n 位的十进制数字 num,小蓝可以从 num 中选出一段连续的子串并将子串进行反转,最多反转一次。小蓝想要将选出的子串进行反转后再放入原位置处得到的新的数字 numnew 满足条件 numnew < num,请你帮他计算下一共有多少种不同的子串选择方案,只要两个子串在 num 中的位置不完全相同我们就视作是不同的方案。
注意,我们允许前导零的存在,即数字的最高位可以是 0 ,这是合法的。
输入格式
输入一行包含一个长度为 n 的字符串表示 num(仅包含数字字符 0 ∼ 9),
从左至右下标依次为 0 ∼ n − 1。
输出格式
输出一行包含一个整数表示答案。
样例输入
210102
样例输出
8
提示
一共有 8 种不同的方案:
1)所选择的子串下标为 0 ∼ 1 ,反转后的 numnew = 120102 < 210102 ;
2)所选择的子串下标为 0 ∼ 2 ,反转后的 numnew = 012102 < 210102 ;
3)所选择的子串下标为 0 ∼ 3 ,反转后的 numnew = 101202 < 210102 ;
4)所选择的子串下标为 0 ∼ 4 ,反转后的 numnew = 010122 < 210102 ;
5)所选择的子串下标为 0 ∼ 5 ,反转后的 numnew = 201012 < 210102 ;
6)所选择的子串下标为 1 ∼ 2 ,反转后的 numnew = 201102 < 210102 ;
7)所选择的子串下标为 1 ∼ 4 ,反转后的 numnew = 201012 < 210102 ;
8)所选择的子串下标为 3 ∼ 4 ,反转后的 numnew = 210012 < 210102 ;
对于 20% 的评测用例,1 ≤ n ≤ 100 ;
对于 40% 的评测用例,1 ≤ n ≤ 1000 ;
对于所有评测用例,1 ≤ n ≤ 5000 。
解题思路:
中心思想:s[l] > s[r]则满足条件,答案的个数+1。
详细解释:考虑s的所有子串[l,r], l即left,是子串的起始下标,r即right是子串的末尾下标,判断s[l] 和 s[r]的大小关系:
若s[l] > s[r]则该子串反转后,新串<原串,满足条件,答案数+1;
若s[l] = s[r]则将子串区间[l,r]缩小为[l+1,r-1],再判断s[l+1]和s[r-1]的大小关系;
若s[l] < s[r]则该子串反转后,新串>原串,不满足条件。
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
map<string,int>mp;
int main()
{
string s;cin>>s;
int n=s.size();//s的长度
ll sum=0;
for(int len=2;len<=n;len++)
{
for(int j=0;j+len<=n;j++)
{
int l=j,r=j+len-1;
//如果相等一直找 看能否找到后面的比前面的小的数(位置翻转之后都是对应的 有点双指针的感觉)
while(s[l]==s[r]&&l<r) l++,r--;
if(s[l]>s[r]&&l<r) sum++;
}
}
cout<<sum<<endl;
return 0;
}