zoukankan      html  css  js  c++  java
  • HDU 5442 Favorite Donut(暴力 or 后缀数组 or 最大表示法)

    http://acm.hdu.edu.cn/showproblem.php?pid=5442

    题意:
    给出一串字符串,它是循环的,现在要选定一个起点,使得该字符串字典序最大(顺时针和逆时针均可),如果有多个字典序相同的,则输出下标最小的,如果下标也是相同的,则输出顺时针方向的。

    思路:
    用了三种方法:

    ①最简单的,暴力枚举所有情况,需要优化一下,先处理出字符串中字典序最大的单词,然后接下来的起点肯定是这个单词,否则就可以跳过这个起点。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 
     7 int n,mx;
     8 string s, best;
     9 
    10 int main()
    11 {
    12    // freopen("in.txt","r",stdin);
    13     int T;
    14     scanf("%d",&T);
    15     while(T--)
    16     {
    17         scanf("%d",&n);
    18         cin>>s;
    19         mx = -1;
    20         for(int i=0;i<n;i++)
    21             if(s[i]-'a' > mx)  mx = s[i]-'a';
    22         s += s;
    23 
    24         int pos, dir;
    25         best = "";
    26         for(int i=0;i<n;i++)
    27         {
    28             if(s[i]-'a'!=mx)  continue;
    29             string tmp = s.substr(i,n);
    30             if(tmp > best)
    31             {
    32                 pos = i;
    33                 dir = 0;
    34                 best = tmp;
    35             }
    36         }
    37         reverse(s.begin(),s.end());
    38         for(int i=n-1;i>=0;i--)
    39         {
    40             if(s[i]-'a'!=mx)  continue;
    41             string tmp = s.substr(i,n);
    42             if(tmp > best)
    43             {
    44                 pos = n-1-i;
    45                 dir = 1;
    46                 best = tmp;
    47             }
    48             else if(tmp == best)
    49             {
    50                 if(n-1-i<pos)
    51                 {
    52                     pos = n-1-i;
    53                     dir = 1;
    54                 }
    55             }
    56         }
    57         printf("%d %d
    ",pos+1,dir);
    58     }
    59     return 0;
    60 }
    暴力枚举

    ②后缀数组:

    正序复制一遍,求一遍后缀数组,那么sa数组中排名最后的肯定是字典序最大的,此时直接取该值即可。而且此时它一定是最小坐标。

    但是求逆序的时候需要注意一下,此时就不能求最小坐标了,因为逆序了,所以最小坐标在原来的字符串中是最大的,所以此时就要求最大字典序情况下的最小坐标。那么此时就要利用height数组了,从底向上依次遍历sa数组,如果和上一个的公共前缀是>=n的话,此时字典序是相同的,但是上一个字符串的坐标更大。知道height值<n。

      1 #include<iostream>
      2 #include<algorithm>
      3 #include<cstring>
      4 #include<cstdio>
      5 using namespace std;
      6 const int maxn=40000+5;
      7 
      8 int n;
      9 char s[maxn];
     10 char s1[maxn],s2[maxn];
     11 int sa[maxn],t[maxn],t2[maxn],c[maxn];
     12 int Rank[maxn],height[maxn];
     13 
     14 void build_sa(int m)
     15 {
     16     int *x=t,*y=t2;
     17     //基数排序
     18     for(int i=0;i<m;i++)    c[i]=0;
     19     for(int i=0;i<n;i++)    c[x[i]=s[i]]++;
     20     for(int i=1;i<m;i++)    c[i]+=c[i-1];
     21     for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
     22     for(int k=1;k<=n;k<<=1)
     23     {
     24         int p=0;
     25         //直接利用sa数组排序第二关键字
     26         for(int i=n-k;i<n;i++)  y[p++]=i;
     27         for(int i=0;i<n;i++)    if(sa[i]>=k)    y[p++]=sa[i]-k;
     28         //基数排序第一关键字
     29         for(int i=0;i<m;i++)    c[i]=0;
     30         for(int i=0;i<n;i++)    c[x[y[i]]]++;
     31         for(int i=1;i<m;i++)    c[i]+=c[i-1];
     32         for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
     33         //根据sa和y计算新的x数组
     34         swap(x,y);
     35         p=1;
     36         x[sa[0]]=0;
     37         for(int i=1;i<n;i++)
     38             x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
     39         if(p>=n)
     40             break;
     41         m=p;                //下次基数排序的最大值
     42     }
     43 }
     44 
     45 void getHeight(int n)
     46 {
     47     int i,j,k=0;
     48     for(i=1;i<=n;i++)  Rank[sa[i]]=i;
     49     for(i=0;i<n;i++)
     50     {
     51         if(k)  k--;
     52         int j=sa[Rank[i]-1];
     53         while(s[i+k]==s[j+k])  k++;
     54         height[Rank[i]]=k;
     55     }
     56 }
     57 
     58 int main()
     59 {
     60     //freopen("in.txt","r",stdin);
     61     int T;
     62     scanf("%d",&T);
     63     while(T--)
     64     {
     65         scanf("%d",&n);
     66         scanf("%s",s);
     67         for(int i=0;i<n;i++)  s[i+n] = s[i];
     68         s[2*n] = '0';
     69         int tmp = n;
     70         n = 2*n+1;
     71         build_sa(128);
     72         getHeight(n-1);
     73         n = tmp;
     74 
     75         int pos1;
     76         memset(s1,0,sizeof s1);
     77         for(int i=2*n;i>=1;i--)
     78         {
     79             if(sa[i]<n)
     80             {
     81                 pos1 = sa[i];
     82                 break;
     83             }
     84         }
     85         strncpy(s1,s+pos1,n);
     86 
     87         reverse(s,s+2*n);
     88         tmp = n;
     89         n = 2*n+1;
     90         build_sa(128);
     91         getHeight(n-1);
     92         n = tmp;
     93 
     94         int pos2;
     95         memset(s2,0,sizeof s2);
     96         for(int i=2*n;i>=1;i--)
     97         {
     98             if(sa[i]<n)
     99             {
    100                 while(height[i]>n) i--;
    101                 pos2 = n-1-sa[i];
    102                 break;
    103             }
    104         }
    105         strncpy(s2,s+n-1-pos2,n);
    106 
    107         if(strcmp(s1,s2)>0)  printf("%d 0
    ",pos1+1);
    108         else if(strcmp(s1,s2)<0) printf("%d 1
    ",pos2+1);
    109         else
    110         {
    111             if(pos1<=pos2)  printf("%d 0
    ",pos1+1);
    112             else printf("%d 1
    ",pos2+1);
    113         }
    114     }
    115     return 0;
    116 }
    后缀数组

    ③最大表示法:

    参考论文:https://wenku.baidu.com/view/c6c5e7335a8102d276a22fa6.html

    算法的具体思路: 
    (1)开始时,将字符串复制两份,设置两个指针,i=0,j=1. 
    (2)k=0,然后反复迭代直到s[i+k]!=s[j+k] 
    (3)如果k=n,那么返回较小的值,否则看情况滑动指针 

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 const int maxn = 40000+5;
     7 
     8 int n;
     9 char s[maxn];
    10 char s1[maxn],s2[maxn];
    11 
    12 int solve1(char *x)  //最大表示法坐标最小
    13 {
    14     int i = 0, j = 1, k = 0;
    15     while(i<n && j<n && k<n)
    16     {
    17         int t = x[i+k] - x[j+k];
    18         if(!t)  k++;
    19         else
    20         {
    21             if(t>0)  j+=k+1;
    22             else i+=k+1;
    23             if(i==j) j++;
    24             k=0;
    25         }
    26     }
    27     return i<j?i:j;
    28 }
    29 
    30 int solve2(char *x)  //最大表示法且坐标最大
    31 {
    32     int i = 0, j = 1, k;
    33     while(i < n && j < n)
    34     {
    35         while(x[i+k] == x[j+k] && k < n) k++;
    36         if (k == n)  //这个意思就是以i开头的和以j开头的字符串相同
    37         {
    38             int len = abs(i - j);  //len的长度就是循环节
    39             return n - len + i;   //取坐标最大的
    40         }
    41         else
    42         {
    43             int t = x[i+k] - x[j+k];
    44             if(t>0)  j+=k+1;
    45             else i+=k+1;
    46             if(i==j) j++;
    47             k = 0;
    48         }
    49     }
    50     if(j >= n) return i;
    51     return j;
    52 }
    53 
    54 int main()
    55 {
    56     //freopen("in.txt","r",stdin);
    57     int T;
    58     scanf("%d",&T);
    59     while(T--)
    60     {
    61         scanf("%d",&n);
    62         scanf("%s",s);
    63         for(int i=0;i<n;i++)  s[i+n] = s[i];
    64         s[2*n] = '';
    65 
    66         int pos1 = solve1(s);
    67         memset(s1,0,sizeof s1);
    68         strncpy(s1,s+pos1,n);
    69 
    70         reverse(s,s+2*n);
    71         int pos2 = solve2(s);
    72         memset(s2,0,sizeof s2);
    73         strncpy(s2,s+pos2,n);
    74 
    75         int t = strcmp(s1,s2);
    76         if(t>0)  printf("%d 0
    ",pos1+1);
    77         else if(t<0)  printf("%d 1
    ",n-pos2);
    78         else
    79         {
    80             if(pos1+1<=n-pos2)  printf("%d 0
    ",pos1+1);
    81             else printf("%d 1
    ",n-pos2);
    82         }
    83     }
    84     return 0;
    85 }
    最大表示法

     

  • 相关阅读:
    The lexer hack
    How Clang handles the type / variable name ambiguity of C/C++
    python
    基于asp.net + easyui框架,一步步学习easyui-datagrid——界面(一)
    程序员最该看的30本书---------------------------国外编辑推荐
    DirectX 学习经典参考书籍 电子书下载
    基于asp.net+ easyui框架,js提交图片,实现先上传图片再提交表单
    请问JAVA三层架构,持久层,业务层,表现层,都该怎么理解?和MVC三层模型有什么区别
    对java框架的几点认识
    J2EE入门必备
  • 原文地址:https://www.cnblogs.com/zyb993963526/p/7931908.html
Copyright © 2011-2022 走看看