要学习如何分析问题,仔细分析哪些部分可以优化哪些不可以,可能直觉上不可行的算法却有着相当优秀的复杂度。
给你一个由小写字母和?组成的字符串,你可以将?替换为任意字母。问能否使abacaba恰好出现一次。
很容易拐到KMP上去,因为长度小于50,测试小于5000组,可以考虑 O ( n 2 ) O(n^2) O(n2)的做法。
用KMP统计原串中abacaba的数量,若大于一个则不可能,如果等于一个,则将所有?替换为不相关的字母即可。
如果等于0,则枚举起点进行暴力check,看看能否修改出来一个abacaba,注意类似abacab?bacaba的情况,所以要将修改后的字符串再跑一次KMP来统计数量是否恰好等于1。
//#pragma comment(linker, "/STACK:102400000,102400000")
#include
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
//#define int ll
const int maxn=105,inf=0x3f3f3f3f,mod=1000000007;
char w[1005]="abacaba",s[10005],ss[maxn];
int nex[1005];//nex[i]记录w[0:i]串的最大公共串长度
void getnext(char *B,int n)
{
nex[0]=0;
for(int i=1,j=0;i<n;i++)/* n为B串长度 */
{
// j=nex[i-1];j为待计算位前一位对应的最大公共串的下一位(或前一位对应的最大公共串长度)
while(B[j]!=B[i]&&j>0)//若匹配不上,尝试缩小公共子串,因为B串i之前的j个字符(B[i-j]~B[i-1])与开头的j个(B[0]~B[j-1])相同,直接在前面j个字符里找
j=nex[j-1];//找最大公共子串的下一位与B[i]相同的最大公共子串,不能用j--,保证B[0]到B[i-1]这段目前匹配前缀后缀始终相同
if(B[j]==B[i])//公共串下一位字符相同,公共最大长+1
j++;
nex[i]=j;//j为0~i最大公共串长度,若最终B[j]!=B[i],j=0
}
}//https://www.cnblogs.com/c-cloud/p/3224788.html
int kmp(int m,int n)//m为A串长度,n为B串长度
{
int ans=0,cmp=0;
for(int i=0;i<m;i++){
while(cmp>0&&s[i]!=w[cmp])//第cmp个位置匹配失败
cmp=nex[cmp-1];//下一轮从匹配成功串的最大公共串的下一位开始匹配
if(s[i]==w[cmp])//当前字符匹配成功,继续下一位
cmp++;
if(cmp==n){//子串匹配成功
ans++;
cmp=nex[cmp-1];//本题母串分割各部分间可以有公共部分
}
}
return ans;
}
bool check(int k)
{
bool ok=1;
for(int i=0;i<7;i++)
if(s[k+i]!=w[i]&&s[k+i]!='?')
ok=0;
if(ok)
for(int i=0;i<7;i++)
s[k+i]=w[i];
return ok;
}
signed main()
{
int t,n;
getnext(w,7);
// string s;
cin>>t;
while(t--)
{
bool ok=0;
cin>>n>>ss;
strcpy(s,ss);
int tot=kmp(n,7);
if(tot==1)
{
cout<<"YES"<<endl;
for(int i=0;i<n;i++)
cout<<(s[i]!='?'?s[i]:'d');
cout<<endl;
}
else if(tot>1)
cout<<"NO"<<endl;
else{
for(int i=0;i<=n-7;i++)
{
strcpy(s,ss);
if(check(i)&&kmp(n,7)==1)
{
ok=1;
cout<<"YES"<<endl;
for(int i=0;i<n;i++)
cout<<(s[i]!='?'?s[i]:'d');
cout<<endl;
break;
}
}
if(!ok)
cout<<"NO"<<endl;
}
}
return 0;
}
m = n × a + b − c m=n\times a+b-c m=n×a+b−c, l ≤ a , b , c ≤ r l\le a,b,c \le r l≤a,b,c≤r,现在给出 l , r , m l,r,m l,r,m,请确定一组 a , b , c a,b,c a,b,c使得存在一个 n > 0 n>0 n>0的解。
本场最简单的题, n = m − b + c a n=\frac{m-b+c}{a} n=am−b+c,符合要求时 m − b + c m-b+c m−b+c一定被 a a a整除。可以使 m m m加上或减去一个数,使它能被 a a a整除,可以发现这个数只和 c − b c-b c−b的差值有关。所以我们可以枚举 a a a,同时 O ( 1 ) O(1) O(1)枚举此时的 b b b和 c c c,并尽量让他们落在 [ l , r ] [l,r] [l,r]内。
//#pragma comment(linker, "/STACK:102400000,102400000")
#include
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
#define int ll
const int maxn=2e5+10,inf=0x3f3f3f3f,mod=1000000007;
signed main()
{
ll t,l,r,m;
cin>>t;
while(t--)
{
cin>>l>>r>>m;
int a,b,c;
for(a=l;a<=r;a++)
{
int res=m%a;
b=l+res,c=l;
if(b<=r&&m>a)
break;
if(res>0)
res=a-res;
b=l,c=l+res;
if(c<=r)
break;
}
cout<<a<<' '<<b<<' '<<c<<endl;
}
return 0;
}
一个卖花的商店有 m m m种花,每一种买第一朵时有 a i a_i ai点贡献,之后每买一朵有 b i b_i bi的贡献,求买 n n n朵的最大贡献。
可以发现如果取了一个 b b b,则之后只会不断取这个 b b b,因为取 b b b就是因为没有更大的 a a a可取了,同时有多个 b i b_i bi可以取的话我们毫无疑问会取最大的那个。
但是只记录最大的 b b b和其对应的 a a a并不一定能保证得到最优解。考虑枚举不断选择的 b i b_i bi,每次将大于 b i b_i bi的 a a a取完,再来取 a i a_i ai和 b i b_i bi。
可以用前缀和+二分来快速得到大于 b i b_i bi的 a a a的和,复杂度 O ( m l o g m ) O(mlogm) O(mlogm)。
//#pragma comment(linker, "/STACK:102400000,102400000")
#include
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
//#define int ll
const int maxn=1e5+10,inf=0x3f3f3f3f,mod=1000000007;
struct node
{
int a,b;
bool vis;
node(){}
node(int a,int b,bool v=0):
a(a),b(b),vis(v){}
// friend bool operator<(const node &x,const node &y)
// {//排在后面
// return ((x.vis)?x.b:x.a)<((y.vis)?y.b:y.a);
// }
} arr[maxn];
ll sum[maxn];
signed main()
{
int t,a,b,n,m;
cin>>t;
while(t--)
{
cin>>n>>m;
// priority_queueq;
for(int i=1;i<=m;i++)
{
cin>>a>>b;
arr[i].a=a,arr[i].b=b;
// q.push({a,b,0});
}
vector<ll>tmp(m);
for(int i=0;i<m;i++)
tmp[i]=arr[i+1].a;
sort(tmp.begin(),tmp.end());
for(int i=1;i<=m;i++)
sum[i]=sum[i-1]+tmp[i-1];
// sort(arr+1,arr+m+1,[](const node &x,const node &y){
// return x.a>y.a;
// });
ll ans=0;
for(int i=1;i<=m;i++)
{
ll cnt=m-(lower_bound(tmp.begin(),tmp.end(),arr[i].b)-tmp.begin());
ll now=sum[m]-sum[m-cnt];
if(cnt>=n)
{
ans=max(ans,sum[m]-sum[m-n]);//选最大的n个a
continue;
}
// for(int j=1;j<=m&&j<=n&&arr[j].a>=arr[i].b;j++)
// now+=arr[j].a,cnt++;
if(arr[i].a<arr[i].b)//未选到i
now+=arr[i].b*(n-cnt-1)+arr[i].a;
else
now+=arr[i].b*(n-cnt);
// printf("cnt=%lld,now=%lld\n",cnt,now);
ans=max(ans,now);
}
cout<<ans<<endl;
}
return 0;
}
/*
可以发现只能取一种b
思路一:优先队列大模拟,不能保证最优,存在b很大但a很小的情况
只记录最大b也是不行的,同样不能保证最优
思路二:枚举每一个b,或者不选b,则强行选择这个b+a和大于他的a
复杂度m^2
*/