[題目記錄]一本通高手訓練-石環

youlv發表於2024-12-07

題意

有一個首尾相連的環 , 元素依次是 \(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();}
}


總結

  • 進行一切能簡化問題的轉化 , 以給後面看出真正的做法做鋪墊 .
  • 先考慮簡單的不包含首尾的部分 , 然後只研究首尾相等 , 得出正確的方法 .

相關文章