zoukankan      html  css  js  c++  java
  • 第三周训练总结

    一、KMP+最大最小值表示法

    最大最小值表示法

    最大最小表示法用于解决字符串的同构问题,其在复杂度为$ O(n) $的时间内求出一个字符串的所有同构串中字典序最大(小)的串的起始位置。

    应用:

    • 给出$ n $个循环字符串判断有多少不同字符串:逐个用最大(小)表示法表示,然后加入 (Set) 去重
    • 循环字符串所有同构串中字典序最大(小)的表示:用最大(小)表示法求出起始位置,输出即可
    • 判断两个字符串是否同构:将两字符串用最大(小)表示法表示,然后逐个字符比较

    原理:

    设一字符串 (S),且 $S’ (是) S $的循环同构的串的最小表示,那么对于字符串循环同构的最小表示法,其问题实质是求 S 串的一个位置,从这个位置开始循环输出 (S),得到的 $S’ $字典序最小。

    最朴素的算法是设$ i(、)j (两个指针,)i (指向最小表示的位置,)j $作为比较指针。

    (i=0)(j=1),那么:

    • (S[i]>S[j]),则:(i=j)(j=i+1)
    • (S[i]<S[j]),则:(j++)
    • (S[i]=S[j]),则设指针 (k),分别从 $i $和 $j (位置向下比较,直到) S[i]!=S[j]$
    • (S[i+k]>S[j+k]),则:(i=j)(j=i+1)
    • 否则$ j++$

    最后返回 $i $即可

    可以看出,朴素算法在 $S[i]=S[j] (时,)i $的指针移动的太少了,在遇到像 $bbb…bbbbbba $这样复杂的字符串时,时间复杂度可能会退化到 (O(n^2)),针对这一问题加以改进可得到 (O(n)) 的最小表示法的算法,其核心思路是在$ S[i]=S[j]$ 时同时维护 (i)、$j $两个指针

    同样令 (i=0)(j=1),那么:

    • (S[i]>S[j]),则:(i=j)(j=i+1)
    • (S[i]<S[j]),则:(j++)
    • 若$ S[i]=S[j](,则设指针) k(,分别从) i (和) j $位置向下比较,直到 (S[i]!=S[j])
    • (S[i+k]>S[j+k]),则:(i=i+k)
    • 否则 (j++)

    最后返回$ i $和 $j $的小者即可

    1、最小值表示法

    int minmumRepresentation(char *str)	//最小表示法
    {
        int len=strlen(str);
        int i=0;						//指向字符串最小的位置
        int j=1;						//比较指针
        int k=0;			//str[i]与str[j]相等时一次移动几个
        while(i<len&&j<len&&k<len)
        {
            int temp=str[(i+k)%len]-str[(j+k)%len];//比较值的长度
            if(temp==0)
                k++;
            else
            {
                if(temp>0)				//维护i
                    i=i+k+1;
                else					//维护j
                    j=j+k+1;
                if(i==j)				//相等时比较指针后移
                    j++;
                k=0;
            }
        }
        return i<j?i:j;					//返回i、j中较小的那个
    }
    
    

    2、最大值表示法

    int maxmumRepresentation(char *str)	//最大表示法
    {
        int len=strlen(str);
        int i=0;						//指向字符串最小的位置
        int j=1;						//比较指针
        int k=0;			//str[i]与str[j]相等时一次移动几个
        while(i<len&&j<len&&k<len)
        {
            int temp=str[(i+k)%len]-str[(j+k)%len];//比较值的长度
            if(temp==0)
                k++;
            else
            {
                if(temp>0)				//维护i
                    j=j+k+1;
                else					//维护j
                    i=i+k+1;
                if(i==j)				//相等时比较指针后移
                    j++;
                k=0;
            }
        }
        return i<j?i:j;					//返回两者最小值
    }
    
    

    HDU-3374解题思路

    首先判定是否是循环串,然后用最小表示法和最大表示法来找出对应的位置,最小表示法可以找出比如一个环形的字符串,找出某个字符开始,然后这个字符串字典序最小

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<string>
    #include<cstring>
    #include<cmath>
    #include<ctime>
    #include<algorithm>
    #include<utility>
    #include<stack>
    #include<queue>
    #include<vector>
    #include<set>
    #include<map>
    #define PI acos(-1.0)
    #define E 1e-9
    #define INF 0x3f3f3f3f
    #define LL long long
    const int MOD=10007;
    const int N=1000000+5;
    const int dx[]= {-1,1,0,0};
    const int dy[]= {0,0,-1,1};
    using namespace std;
    int Next[N];
    char str[N];
    void getNext(char p[])
    {
        Next[0]=-1;
        int len=strlen(p);
        int j=0;
        int k=-1;
    
        while(j<len)
        {
            if(k==-1||p[j]==p[k])
            {
                k++;
                j++;
                Next[j]=k;
            }
            else
            {
                k=Next[k];
            }
        }
    }
    int minmumRepresentation(char *str) //最小表示法
    {
        int len=strlen(str);
        int i=0;
        int j=1;
        int k=0;
        while(i<len&&j<len&&k<len)
        {
            int temp=str[(i+k)%len]-str[(j+k)%len];
            if(temp==0)
                k++;
            else
            {
                if(temp>0)
                    i=i+k+1;
                else
                    j=j+k+1;
                if(i==j)
                    j++;
                k=0;
            }
        }
        return i<j?i:j;
    }
    int maxmumRepresentation(char *str) //最大表示法
    {
        int len=strlen(str);
        int i=0;
        int j=1;
        int k=0;
        while(i<len&&j<len&&k<len)
        {
            int temp=str[(i+k)%len]-str[(j+k)%len];
            if(temp==0)
                k++;
            else
            {
                if(temp>0)
                    j=j+k+1;
                else
                    i=i+k+1;
                if(i==j)
                    j++;
                k=0;
            }
        }
        return i<j?i:j;
    }
    
    
    int main()
    {
        while(scanf("%s",str)!=EOF)
        {
            getNext(str);
    
            int n=strlen(str);
            int len=n-Next[n];
    
            int num=1;//数量
            if(n%len==0)
                num=n/len;
    
            int minn=minmumRepresentation(str);//最小表示
            int maxx=maxmumRepresentation(str);//最大表示
    
            printf("%d %d %d %d
    ",minn+1,num,maxx+1,num);
        }
        return 0;
    }
    
    

    二、

  • 相关阅读:
    关于C++名字空间
    选择组合OR继承?
    编译器为C++ 空类自动生成的成员函数
    函数返回值为引用类型
    关于数据库存储过程分页DatagridView BindingNavigator 控件的详细实现
    ADO.NET 安全编码指南 来自MSDN
    ADO.NET中调用存储过程
    视图
    高效使用连接的模式
    GROUP BY, HAVING, COMPUTE, ORDER BY 语句
  • 原文地址:https://www.cnblogs.com/trirabbits/p/11514631.html
Copyright © 2011-2022 走看看