最小表示法


BZOJ_2882_工藝

給出一個字符串,求與它循環同構的串中字典序最小的串。


后綴數組/后綴自動機+map 都可以在O(nlogn)的時間復雜度求出。

實際上有一個專門解決這類問題的算法:最小表示法。

首先把串復制一遍貼在原串后面,這樣每個循環同構的串可以用S[i]~S[i+n-1]來表示,設為w(i)。

也就是說我們把所有的串拿出來了,比較就行了。

在比較w(i)和w(j)時的最壞時間復雜度是O(n)的,也就是說這只是一個暴力的做法。

實際上我們不需要對所有的w(i)都進行一次比較。

假設比較w(i)和w(j)時比較了k個字符,直到k+1個字符不同。

那么我們將字典序大的那邊指針向后跳k+1即可,因為已經知道有比這些串小的串了(就在另一個指針的后面)

相當於每個指針最多向后跳n次,復雜度就變成O(n)的了,非常好寫。

代碼:

while(i<=n&&j<=n) {
        for(k=0;k<n&&w[i+k]==w[j+k];k++);
        if(k==n) break;
        if(w[i+k]>w[j+k]) {
            i+=k+1; if(i==j) i++;
        }else {
            j+=k+1; if(i==j) j++;
        }
    }
    i=min(i,j);

 BZOJ_2882代碼:

#include <cstdio>
#include <string.h>
#include <algorithm>
using namespace std;
inline char nc() {
    static char buf[100000],*p1,*p2;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd() {
    int x=0; char s=nc();
    while(s<'0'||s>'9') s=nc();
    while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+s-'0',s=nc();
    return x;
}
char pbuf[10000000] , *pp = pbuf;
inline void write(int x)
{
    static int sta[35];
    int top = 0;
    if(!x)sta[++top]=0;
    while(x) sta[++top] = x % 10 , x /= 10;
    while(top) *pp ++ = sta[top -- ] ^ '0';
}
#define N 600050
int w[N],n;
int main() {
    n=rd();
    register int i;
    for(i=1;i<=n;i++) w[i]=rd(),w[i+n]=w[i];
    register int j=2,k; i=1;
    while(i<=n&&j<=n) {
        for(k=0;k<n&&w[i+k]==w[j+k];k++);
        if(k==n) break;
        if(w[i+k]>w[j+k]) {
            i+=k+1; if(i==j) i++;
        }else {
            j+=k+1; if(i==j) j++;
        }
    }
    i=min(i,j);j=i+n-1;
    while(i<=j) write(w[i]),*pp++=' ',i++;
    fwrite(pbuf,1,pp-pbuf,stdout);
    return 0;
}

 BZOJ_1398_Vijos1382尋找主人 Necklace

題意:判斷兩個串是否循環同構。

分析:分別求兩個串的最小表示,然后比較即可,時間復雜度O(n)。

代碼:

#include <cstdio>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 2000050
int n;
int a[1000050];
char w[N];
int main() {
	int i;
	scanf("%s",w+1);
	int n=strlen(w+1);
	for(i=1;i<=n;i++) w[i+n]=w[i];
	int j=2,k; i=1;
	while(i<=n&&j<=n) {
		for(k=0;k<n&&w[i+k]==w[j+k];k++) ;
		if(k==n) break;
		if(w[i+k]>w[j+k]) {
			i+=k+1; if(i==j) i++;
		}else {
			j+=k+1; if(i==j) j++;
		}
	}
	i=min(i,j);
	for(k=1;k<=n;k++) a[k]=w[i+k-1];
	scanf("%s",w+1);
	if(strlen(w+1)!=n) {
		puts("No"); return 0;
	}
	for(i=1;i<=n;i++) w[i+n]=w[i];
	j=2;i=1;
	while(i<=n&&j<=n) {
		for(k=0;k<n&&w[i+k]==w[j+k];k++) ;
		if(k==n) break;
		if(w[i+k]>w[j+k]) {
			i+=k+1; if(i==j) i++;
		}else {
			j+=k+1; if(i==j) j++;
		}
	}
	i=min(i,j);
	for(k=1;k<=n;k++) if(a[k]!=w[i+k-1]) {
		puts("No"); return 0;
	}
	puts("Yes");
	for(i=1;i<=n;i++) printf("%c",a[i]);
}

 總結(來自周源《淺析“最小表示法”思想在字符串循環同構問題中的應用》,我認為說得很好):

“最小表示法”是判斷兩種事物本質是否相同的一種常見思想,它的通用性也是被人們認可的——無論是搜索中判重技術,還是判斷圖的同構之類復雜的問題,它都有着無可替代的作用。仔細分析可以得出,其思想精華在於引入了“序”這個概念,從而將紛繁的待處理對象化為單一的形式,便於比較。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM