zoukankan      html  css  js  c++  java
  • 字符串最小表示法

    参考资料:

    https://oi-wiki.org/string/minimal-string/

    https://blog.csdn.net/w4149/article/details/76254421


    问题:给出一个字符串(s),求出这个字符串的所有循环同构串中字典序最小的。

    首先有个暴力(直接复制oi-wiki的标):

    大概是两个指针(i,j)。记(k)为公共前缀,尝试扩展(k)到不能扩展为止。如果(k<n)则可以得到两个指针开头的循环同构串的大小关系,将比较大的右移,继续做。

    int k = 0, i = 0, j = 1;
    while (k < n && i < n && j < n) {
      if (sec[(i + k) % n] == sec[(j + k) % n]) {
        ++k;
      } else {
        if (sec[(i + k) % n] > sec[(j + k) % n])
          ++i;
        else
          ++j;
        k = 0;
        if (i == j) i++;
      }
    }
    i = min(i, j);
    

    这个暴力显然是(O(n^2))的。

    (S_{ldots r})一段区间的字符串,(S_{ldots l+n-1})考虑(S_{idots i+k-1}=S_{j dots j+k-1})(S_{i+k}>S_{j+k}),对于任意(lin [0,k]),都有(S_{i+ldots i+l+n-1}>S_{j+ldots j+l+n-1})。于是(i)可以直接跳到(i+k+1),当然由于小于等于(j)的都被检验过不是最小的了(否则(i)(j)必然有个被替换),所以是跳到(max(i+k+1,j+1))

    然后这样就是(O(n))的了。


    https://www.cnblogs.com/jz-597/p/13966386.html

    这题用最小表示法来判断相等。

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <climits>
    #define N 105
    #define ll long long
    #define mo 998244353
    #define jd 19260817
    ll qpow(ll x,ll y){
    	ll r=1;
    	for (;y;y>>=1,x=x*x%mo)
    		if (y&1)
    			r=r*x%mo;
    	return r;
    }
    int n;
    int e[N][N],ne[N];
    int re[N][N];
    int f[N][N][N];
    int vis[N];
    int idc[N];
    int minidc(int a[],int n){
    	int i=0,j=1,k=0;
    	while (i<n && j<n){
    		for (k=0;k<n && a[(i+k)%n]==a[(j+k)%n];++k)
    			k++;
    		if (k==n) break;
    		if (a[(i+k)%n]>a[(j+k)%n])
    			i=max(i+k+1,j+1);
    		else
    			j=max(j+k+1,i+1);
    	}
    	i=min(i,j);
    	ll res=0;
    	for (int j=0;j<n;++j)
    		res=(res*jd+a[(i+j)%n])%mo;
    	return res;
    }
    bool same(int x,int y){
    	if (ne[x]!=ne[y]) return 0;
    	return idc[x]==idc[y];
    }
    int main(){
    //	freopen("in.txt","r",stdin);
    	scanf("%d",&n);
    	for (int i=1;i<=n;++i){
    		scanf("%d",&ne[i]);
    		for (int j=0;j<ne[i];++j){
    			scanf("%d",&e[i][j]);
    			re[i][e[i][j]]=j;
    		}
    	}
    	for (int i=1;i<=n;++i)
    		for (int j=0;j<ne[i];++j)
    			f[0][i][j]=ne[i];
    	for (int k=1;k<=n;++k)
    		for (int i=1;i<=n;++i)
    			for (int j=0;j<ne[i];++j){
    				int y=e[i][j];
    				ll key=0;
    				for (int t=re[y][i];t<ne[y];++t)
    					key=(key*jd+f[k-1][y][t])%mo;
    				for (int t=0;t<re[y][i];++t)
    					key=(key*jd+f[k-1][y][t])%mo;
    				f[k][i][j]=key;
    			}
    	for (int i=1;i<=n;++i)
    		idc[i]=minidc(f[n][i],ne[i]);
    //	for (int i=1;i<=n;++i){
    //		printf("i=%d : %d
    ",i,idc[i]);
    //		for (int j=0;j<ne[i];++j)
    //			printf("%d ",f[n][i][j]);
    //		printf("
    ");
    //	}
    	int bz=0;
    	for (int i=1;i<=n;++i)
    		if (!vis[i]){
    			vis[i]=1;
    			static int q[N];
    			int k=1;
    			q[1]=i;
    			for (int j=i+1;j<=n;++j)
    				if (!vis[j] && same(i,j)){
    					vis[j]=1;
    					q[++k]=j;
    				}
    			if (k>1){
    				for (int j=1;j<=k;++j)
    					printf("%d ",q[j]);
    				printf("
    ");
    				bz=1;
    			}
    		}
    	if (bz==0)
    		printf("none
    ");
    	return 0;
    }
    
  • 相关阅读:
    Reasoning and Learing学习笔记
    Study in JI During the Summer Vacation
    2018-计算机系机试-A
    2018-计算机系机试(第二批)-E-绝对值排序
    2018-计算机系机试(第二批)-D-最小差值
    2018-计算机系机试(第二批)-C-数字字符个数
    2018-计算机系机试(第二批)-B-二进制输出
    2018-计算机系机试(第二批)-A-最大数
    2018-软工机试-E-热河路(TLE只拿了90分,待思考)
    2015年四川省赛
  • 原文地址:https://www.cnblogs.com/jz-597/p/13966523.html
Copyright © 2011-2022 走看看