传送门
题意分析:
给出两个字符串 S T 可以通过删除S中的一些子序列(连续)使得最后获得T,问删除子序列的最大长度;
样例一
bbaba
bb
输出 3
可以把 aba删除得到bb
样例二
baaba
ab
输出 2
把 ba a 删除得到ab 所以最大子序列长度是2
思路
比赛的时候只是爆出了D1而D2没有太多的想法,结束后看到cf给出的标签是binary search greedy 就有点想法了,可以二分答案,二分出最长能删除的连续子序列,那么怎么二分呢? 从1-n遍历第一个字符串 记i为循环变量, 那么左端点为i右端点为n 取mid=(l+r)>>1如果mid此时符合题意那么此时能删除的子序列长度为ans=mid-i+1这样问题就很清楚了,只剩下一个问题了 怎么判断此时的mid符合题意呢? 我们不妨这样想 取一个数组f[]记录第一个字符串当前位置及以前的位置能匹配到第二个字符串的最大个数,然后再反过来记录另外一个数组e[] 记录第一个字符串当前位置向后能匹配到第二个字符串的最大个数 ,好了 。到这里我们来分析下这两个数组的用处 就是用来验证mid是否符合题意的 我们二分的要删除的区间为(i,mid) 所以我们只需判断i之前与第二个字符串相匹配的个数和mid之后与第二个字符串相匹配的个数之和是否大于第一个字符串即可。不会出现重复 仔细想想 因为i肯定小于mid 这样这道题就解出来了。其实这个求前缀和后缀的思想,我根本就没想到,而有个学长念一遍题就知道前缀+后缀+二分 不得不佩服他,tql,orz 哈哈 但是仔细想想自己的漏洞,还是二分不会用只会简单的二分答案验证,那如果要在一段区间中找到一个满足某种条件的尽有可能大或者小的值但是这种值也有可能不存在,还是慢慢刷题遇到解决吧, 哈哈
代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<cmath>
#define Xiaobo main
#define IO cin.tie(0),ios::sync_with_stdio(false);
using namespace std;
const int maxn=2e5+5;
const int mod=1e9+7;
const double eps=1e-15;
const double pi=acos(-1);
const int INF=0x3f3f3f;
typedef long long ll;
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
ll qpow(ll a,ll b,ll m){ ll ans=1; while(b){ if(b&1) ans=ans%a%m;a=a*a%m,b>>=1;} return ans; }
ll qpow(ll a,ll b){ ll ans=1;while(b>0){ if(b&1) ans=ans*a;a*=a,b>>=1; } return ans;}
ll gcd(ll a,ll b){ return b==0?a:gcd(b,a%b);}
ll mul(ll a,ll b,ll m){ll res=0;while(b>0) { if(b&1) res=(res+a)%m;a=(a+a)%m;b>>=1;}return res;}
int n,m;
int f[maxn],e[maxn];
char s[maxn],t[maxn];
int Xiaobo()
{
cin>>s+1>>t+1;
int len1=strlen(s+1);
int len2=strlen(t+1);
for(int i=1;i<=len1;i++) {//构造前缀数组
f[i]=f[i-1];
if(f[i]==len2) continue;
if(s[i]==t[f[i]+1]) {
f[i]++;
}
}
for(int i=len1;i>=1;i--) {//构造后缀数组
e[i]=e[i+1];
if(e[i]==len2) continue;
if(s[i]==t[len2-e[i]]) {
e[i]++;
}
}
int ansmax=0;
for(int i=1;i<=len1;i++) {//二分答案
int l=i,r=len1,ans=-1;
while(l<=r) {
int mid=(l+r)>>1;
if(f[i-1]+e[mid+1]>=len2) {
ans=mid;
l=mid+1;
}
else r=mid-1;
}
if(ans!=-1) ansmax=max(ansmax,ans-i+1);
}
cout<<ansmax<<endl;
}
附上一位学长的ac代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int a[200010],b[200010];
int len1,len2;
string s,t;
int mmax;
int dfs(int w){
if(b[w]>=len2){
return 1;
}
if(a[len1-w-1]>=len2){
return 1;
}
for(int i=1;i+w<len1;i++){
if(a[i-1]+b[i+w]>=len2){
return 1;
}
}
return 0;
}
int main(){
while(cin>>s>>t){
mmax=0;
len1=s.length();
len2=t.length();
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
int l=len1-1,r=len2-1;
while(l>=0){
b[l]=b[l+1];
if(r>=0 && s[l]==t[r])
b[l]++,r--;
l--;
}
l=0;
r=0;
while(l<len1){
if(l>0)
a[l]=a[l-1];
if(r<len2 && s[l]==t[r])
a[l]++,r++;
l++;
}
l=0,r=len1;
while(l<=r){
int mid=(l+r)>>1;
if(dfs(mid)){
l=mid+1;
}
else
r=mid-1;
}
printf("%d\n",l-1);
}
}
接下来就是C
题意简单,通俗理解哈哈哈 求一下所有数的最大公约数,然后欧拉求该公约数的所有因子个数即可
附上Ac代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<cmath>
#define Xiaobo main
#define IO cin.tie(0),ios::sync_with_stdio(false);
using namespace std;
const int maxn=4e5+5;
const int mod=1e9+7;
const double eps=1e-15;
const double pi=acos(-1);
const int INF=0x3f3f3f;
typedef long long ll;
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
ll qpow(ll a,ll b,ll m){ ll ans=1; while(b){ if(b&1) ans=ans%a%m;a=a*a%m,b>>=1;} return ans; }
ll qpow(ll a,ll b){ ll ans=1;while(b>0){ if(b&1) ans=ans*a;a*=a,b>>=1; } return ans;}
ll gcd(ll a,ll b){ return b==0?a:gcd(b,a%b);}
ll mul(ll a,ll b,ll m){ll res=0;while(b>0) { if(b&1) res=(res+a)%m;a=(a+a)%m;b>>=1;}return res;}
int n,m;
ll num[maxn];
int Xiaobo()
{
cin>>n;
ll gcd_;
cin>>gcd_;
for(int i=2;i<=n;i++) {
cin>>num[i];
gcd_=gcd(gcd_,num[i]);
}
ll s=1;
for(ll j=2;j*j<=gcd_;j++) {
if(gcd_%j==0) {
ll a=0;
while(gcd_%j==0) {
gcd_/=j;
a++;
}
s*=(a+1);
}
}
if(gcd_>1) s=s*2;
cout<<s<<endl;
}
附上 欧拉函数求一个数因子个数的模板
#include<iostream>
using namespace std;
int main()
{
int n;
cin >> n;
int num = 1,a;
for(int i = 2; i * i <= n; i++)
if(n % i == 0) {
a = 0;
while(n % i == 0) a++,n /= i;
num *= (1 + a);
}
if(n > 1) num *= 2;
cout << num << endl;
return 0;
}
因子数之和模板
#include<iostream>
using namespace std;
int main()
{
int n;
cin >> n;
int sum = 1,a;
for(int i = 2; i * i <= n; i++)
if(n % i == 0) {
a = 1;
while(n % i == 0) a *= i,n /= i;
sum *= (a * i - 1) / (i - 1);//等比数列求和
}
if(n > 1) sum *= (1 + n);
cout << sum << endl;
return 0;
}
加油喽 慢慢努力!!!