題意
有一個首尾相連的環 , 元素依次是 \(a_1 \cdots a_n\) .
對於每個 \(0\le k< n\) , 回答是否存在刪除 \(k\) 個相鄰元素的方案 , 使得刪除後的環相鄰元素不相等 ( 包括首尾元素 ) .
\(n\le 10^6\) .
題解
必要地簡化一下問題 , 先把原串複製一遍接在後面表示環 , 刪除 \(k\) 個相當於在新的串上保留一個 \(n-k\) 的區間 . 因此只考慮是否能找出長為 \(k\) 的子區間使相鄰元素不相等 .
先考慮除了首尾之外不相等 , 發現原來串上相鄰相等的位置不可能出現在同一個區間裡 , 在這樣的位置分段 , 可以把原串分成若干段 , 保證在這些段內取除首尾之外相等 .
然後只需保證首尾的元素不相等 , 發現對於長度 \(k\) , 所有長度 \(k\) 的首尾都相等 , 相當於長度為 \(len-k+1\) 的前字尾相等 , 於是轉化成 $\mathrm{KMP} $ 問題 .
點選檢視程式碼
#include<bits/stdc++.h>
#define file(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define ll long long
#define INF 0x7fffffff
#define INF64 1e18
using namespace std;
constexpr int N=2e6+5;
int n,f[N];
string s;
int res[N];
void check(int l,int r){
f[1]=0;
int j=0;
for(int i=2;i<=r-l+1;i++){
while(j&&s[l-1+j]!=s[l+i-2]) j=f[j];
if(s[l-1+j]==s[l+i-2]) j++;
f[i]=j;
}
for(int i=1;i<=r-l+1;i++) res[i]++;
while(j) res[r-l+1-j+1]--,j=f[j];
}
int cnt;
void solve(){
memset(res,0,sizeof res);
n=s.length();
s=s+s;
int last=1;
for(int i=1;i<2*n;i++){
if(s[i]==s[i-1]){
check(last,i);
last=i+1;
}
}
check(last,2*n);
cout<<"Case "<<cnt<<": ";
for(int i=0;i<n;i++)
if(res[n-i]) cout<<1;
else cout<<0;
cout<<'\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
while(cin>>s){ cnt++;solve();}
}
總結
- 進行一切能簡化問題的轉化 , 以給後面看出真正的做法做鋪墊 .
- 先考慮簡單的不包含首尾的部分 , 然後只研究首尾相等 , 得出正確的方法 .