【題解】Codeforces19C. Deletion of Repeats 後綴數組

給定一個字符串,長爲n(1e5)n(1e5),字符集1e91e9,保證每個字符最多出現10次。

定義重複:長爲偶數的子串,左半部分和右半部分完全相等。

每次選擇最短、最靠左的一個重複,刪除它的左半部分,以及這個重複左邊的所有字符。

輸出沒有重複時的字符串。


每個字符最多出現10次,所以重複的個數一定不會超過5e55e5

處理所有字符出現的位置,通過lcp判斷是否出現重複,就找到了所有的重複。

對所有重複排序,然後遍歷一遍,如果一個重複是合法的(子串沒有被刪掉一部分),就將其應用(刪掉)。

後綴數組板子題吧。


我的垃圾後綴數組,居然不支持\0作爲字符。

qwq。

建議先離散化。

/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 100016, MOD = 1000000007;

namespace SA
{
    void st_init(int *arr, int n);
    
    /* 後綴數組 */
    int sa[M], rk[M], height[M]; //後綴三數組,sa和rk下標從0開始,height下標從1開始
    int t1[M], t2[M], c[M]; // 用於基數排序的三個輔助數組
    void build(int *str, int n, int m) // 構造後綴三數組,字符串下標從0開始,n表示長度,m表示字符集大小
    {  
        str[n] = 0;
        n++;  
        int i, j, p, *x = t1, *y = t2;  
        for(i = 0; i < m; i++) c[i] = 0;  
        for(i = 0; i < n; i++) c[x[i]=str[i]]++;  
        for(i = 1; i < m; i++) c[i] += c[i-1];  
        for(i = n-1; i >= 0; i--) sa[--c[x[i]]] = i;  
        for(j = 1; j <= n; j<<=1)  
        {  
            p = 0;  
            for(i = n-j; i < n; i++) y[p++] = i;  
            for(i = 0; i < n; i++) if(sa[i] >= j) y[p++] = sa[i]-j;  
            for(i = 0; i < m; i++) c[i] = 0;  
            for(i = 0; i < n; i++) c[x[y[i]]]++;  
            for(i = 1; i < m; i++) c[i] += c[i-1];  
            for(i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];  
            swap(x, y);  
            p = 1; x[sa[0]] = 0;  
            for(i = 1; i < n; i++)  
                x[sa[i]] = (y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]) ? p-1 : p++;  
            if(p >= n) break;  
            m = p;  
        }  
        n--;  
        for(int i = 0; i <= n; i++) rk[sa[i]] = i;  
        // for(int i=0; i<=n; ++i)
        // {
        // 	printf("%d %d %d\n",i,sa[i],rk[i] );
        // }
        for(int i=0, j=0, k=0; i < n; i++)  
        {   
            if(k) k--;  
            j = sa[rk[i]-1];  
            while(str[i+k]==str[j+k]) k++;  
            height[rk[i]] = k;  
        }  
        st_init(height, n);
    }

    /* ST表 */
    int lg[M], _n;
    int table[20][M];
    void st_init(int *arr, int n)
    {
        _n = n;
        if(!lg[0])
        {
            lg[0]=-1;
            for(int i=1;i<M;i++)
                lg[i]=lg[i/2]+1;
        }
        for(int i=1; i<=n; ++i)
            table[0][i] = arr[i];
        for(int i=1; i<=lg[n]; ++i)
            for(int j=1; j<=n; ++j)
                if(j+(1<<i)-1 <= n)
                    table[i][j] = min(table[i-1][j], table[i-1][j+(1<<(i-1))]);
    }
    // 查詢第l個後綴和第r個後綴的LCP,下標從0開始
    int lcp(int l, int r)
    {
        if(l==r) return _n-l;
        l = rk[l], r = rk[r];
        if(l>r) swap(l,r);
        ++l;

        int t = lg[r-l+1];
        return min(table[t][l], table[t][r-(1<<t)+1]);
    }
};

int save[M];
//離散化
int dct[M];
int discretization(int n)
{
	int dcnt = 0;
	for(int i=0; i<n; ++i)
		dct[dcnt++] = save[i];
	sort(dct, dct+dcnt);
	dcnt = unique(dct, dct+dcnt) - dct;
	for(int i=0; i<n; ++i)
		save[i] = lower_bound(dct, dct+dcnt, save[i])-dct+1;
	return dcnt;
}
vector<int> ids[M];
int main(void)
{
	#ifdef _LITTLEFALL_
	freopen("in.txt","r",stdin);
    #endif

	int n = read();
	for(int i=0; i<n; ++i)
		save[i] = read();
	int m = discretization(n);
	//printf("save[0]=%d\n",save[0]);
	SA::build(save, n, m+1);
	for(int i=0; i<n; ++i)
		ids[save[i]].push_back(i);

	vector<pair<int,int>> qs; //差,起始位置
	for(int i=1; i<=m; ++i)
		for(auto id1:ids[i]) for(auto id2:ids[i]) if(id1<id2) //lazy
			if(id1<id2 && SA::lcp(id1,id2)>=id2-id1)
				qs.push_back({id2-id1, id1});
	sort(qs.begin(), qs.end());

	int cut = 0; //截取之後從哪裏開始
	for(auto q:qs) if(q.second>=cut)
		cut = q.first+q.second;

	printf("%d\n",n-cut );
	for(int i=cut; i<n; ++i)
		printf("%d ",dct[save[i]-1] );

    return 0;
}

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章