都5202年了你的最小表示法不会还跑得这么慢吧
small_lemon_qwq · · 算法·理论
文章标题中的
暴力
oi-wiki 的代码。
这里换一种写法,对下文帮助更大。
#include<bits/stdc++.h>
using namespace std;
int n,a[600005];
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i+n]=a[i];
}
int ans=1;
for(int i=2;i<=n;i++){
for(int k=0;k<n;k++){
if(a[ans+k]>a[i+k]){
ans=i;
break;
}
if(a[ans+k]<a[i+k])break;
}
}
for(int i=ans;i<ans+n;i++){
cout<<a[i]<<" ";
}
return 0;
}
一般做法
这里讲的不是我的做法,讲的是最常见的
这里插入一条广告。
以这道题为例:【模板】最小表示法。
考虑将输入的序列破环为链,并定义
双指针枚举
给出对于
对于
x\in [0,k] ,那么考虑比较S_{i+x} 与S_{j+x} ,那么由于i+k>i+x 且对于\forall y\in[x,k) ,都有a_{i+y}=a_{j+y} ,那么S_{i+x}>S_{j+x} ,所以S_{i+x} 不是最小表示法。
我只有 Necklace 的代码(以前写的):
#include<bits/stdc++.h>
using namespace std;
#define int long long
string get(string s){
int n=s.size();
if(n==1)return s;
s=" "+s+s;
int i=1,j=2;
while(i<=n&&j<=n){
int k=0;
while(k<n&&s[i+k]==s[j+k])k++;
if(k==n)break;
if(s[i+k]<s[j+k])swap(i,j);
i+=k+1;
if(i==j)i++;
}
return s.substr(min(i,j),n);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
string s,t;
cin>>s>>t;
if(get(s)==get(t)){
cout<<"Yes\n"<<get(s);
}else{
cout<<"No\n";
}
return 0;
}
我的做法
感觉没学过最小表示法的情况下更容易想到这个做法?
将输入的序列破环为链之后,令
- 若
a_{j+k}> a_{i+k} ,执行j\gets i 以及i\gets i+1 ; - 否则
a_{j+k}<a_{i+k} ,和上文一样,执行i\gets i+k+1 即可。
答案就是
代码:
#include<bits/stdc++.h>
using namespace std;
int n,a[600005];
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i+n]=a[i];
}
a[2*n]=31;
int i=2,j=1;
while(i<=n){
int k=0;
while(k<n&&a[i+k]==a[j+k])k++;
if(a[i+k]<a[j+k])j=i,i++;
else i+=k+1;
}
for(int i=j;i<j+n;i++){
cout<<a[i]<<" ";
}
return 0;
}
然后发现和最优解差远了,然后我就 copy 了最优解季军的快读快写,就拿到 rk1 了。