• [NOI2016]优秀的拆分 后缀数组


    题面:洛谷

    题解:

      因为对于原串的每个长度不一定等于len的拆分而言,如果合法,它将只会被对应的子串统计贡献。

      所以子串这个限制相当于是没有的。

      所以我们只需要对于每个位置i求出f[i]表示以i为开头的形如BB这样的串的个数,g[i]表示以i为结尾的形如AA这样的串的个数即可。

      考虑分别处理这2个数组。

      我们可以枚举AA(BB)这样的串中A(B)的长度l,然后把原串每l个字符放在一个块中,在考虑统计答案。

      先考虑这样一个问题:

        假如固定一个串的结尾,再枚举这个串A的长度,怎样可以判断是否合法?

        实际上我们只需要判断我们假定的这个AA串的开头和中间位置(结尾向前走A的长度)的LCP是否可以覆盖开头到中间即可。

      然后如果我们已经把原串对于当前枚举的长度l分成了很多块,其实我们就已经可以对与每个块的开头结尾所代表的点对(i, j)判断是否可以产生贡献了。

      但是怎么统计 其他没有刚好对应在某个块的开头结尾的点对 的贡献呢?

      表示并没有想出来,,,但是感觉有个blog写的很好,,,

      推荐一下:[BZOJ]4650 优秀的拆分(Noi2016)

      以后彻底搞懂了再来填坑吧。

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define R register int
      4 #define AC 301000
      5 #define ac 602000
      6 #define LL long long
      7 #define rev reverse
      8 #define mem(x) memset(x, 0, sizeof(x))
      9 
     10 int T, n, m;
     11 int h[ac], sa[ac], p1[ac], p2[ac], b[ac], d[ac];
     12 int rk[ac], p[AC], t[AC], rk1[ac];
     13 int st1[AC][19], st2[AC][19];
     14 int f[AC], g[AC];
     15 LL ans;
     16 char s[AC];
     17 
     18 void init()
     19 {
     20     for(R i = 1; i <= n; i ++) f[i] = g[i] = rk[i] = 0;//因为有多组数据,所以要全部清空
     21 }//mem这么多次还不如for
     22 
     23 inline void upmax(int &a, int b){
     24     if(b > a) a = b;
     25 }
     26 
     27 inline int Min(int a, int b){
     28     return (a < b) ? a : b;
     29 }
     30 
     31 void pre()
     32 {
     33     scanf("%s", s + 1), n = strlen(s + 1), m = 127;
     34 }
     35 
     36 void ssort()
     37 {
     38     for(R i = 1; i <= n; i ++) ++ d[p2[i]];
     39     for(R i = 1; i <= m; i ++) d[i] += d[i - 1];
     40     for(R i = 1; i <= n; i ++) b[d[p2[i]] --] = i;//给i分配d[p2[i]]的排名
     41     for(R i = 0; i <= m; i ++) d[i] = 0;
     42 
     43     for(R i = 1; i <= n; i ++) ++ d[p1[i]];
     44     for(R i = 1; i <= m; i ++) d[i] += d[i - 1];
     45     for(R i = n; i; -- i) sa[d[p1[b[i]]] --] = b[i];//给b[i]分配d[p1[b[i]]]的排名
     46     for(R i = 0; i <= m; i ++) d[i] = 0;    
     47 }
     48 
     49 void get_sa()
     50 {
     51     for(R i = 1; i <= n; i ++) sa[i] = i, rk[i] = s[i];//初始化
     52     m = 127;//这个也要重置
     53     for(R k = 1; k <= n; k <<= 1)
     54     {
     55         for(R i = 1; i <= n; i ++) p1[i] = rk[i], p2[i] = rk[i + k];
     56         ssort();
     57         int tmp = 1; 
     58         rk[sa[1]] = 1;        
     59         for(R i = 2; i <= n; i ++)
     60             rk[sa[i]] = (p1[sa[i]] == p1[sa[i - 1]] && p2[sa[i]] == p2[sa[i - 1]]) ? tmp : ++ tmp;
     61         if(tmp >= n) break;
     62         m = tmp;//忘了,,,
     63     }
     64 }
     65 
     66 void build()//获取h数组
     67 {
     68     //memset(h, 0, sizeof(h));
     69     for(R i = 1, k = 0; i <= n; i ++)
     70     {
     71         if(k) -- k;
     72         int j = sa[rk[i] - 1];
     73         while(s[i + k] == s[j + k]) ++ k;
     74         h[rk[i]] = k;
     75     }
     76 }
     77 
     78 #define st st1
     79 void build1()//建st1(维护LCP)
     80 {
     81     int tmp = 1, cnt = 0;
     82     memcpy(rk1, rk, sizeof(rk));
     83     for(R i = 1; i <= n; i ++)
     84     {
     85         st[i][0] = h[i];
     86         if(i == tmp << 1) tmp <<= 1, ++ cnt;
     87         p[i] = tmp, t[i] = cnt;
     88     }
     89 }
     90 #undef st
     91 
     92 void build2()//建st2(维护LCS)改成st1, st2一起建了。。。。
     93 {
     94     for(R i = 1; i <= n; i ++) st2[i][0] = h[i];
     95     int tmp = 1;
     96     for(R i = 1; i <= 18; i ++)
     97     {
     98         for(R j = 1; j <= n; j ++)
     99         {
    100             st1[j][i] = Min(st1[j][i - 1], st1[j + tmp][i - 1]);
    101             st2[j][i] = Min(st2[j][i - 1], st2[j + tmp][i - 1]);
    102         }
    103         tmp <<= 1;
    104     }
    105 }
    106 
    107 inline void swap(int &l, int &r)
    108 {
    109     int x = l;
    110     l = r, r = x;
    111 }
    112 
    113 int get1(int l, int r)//查询串l和串r的LCP
    114 {
    115     l = rk1[l], r = rk1[r];
    116     if(l > r) swap(l, r);
    117     ++ l;
    118     int len = r - l + 1;
    119     return Min(st1[l][t[len]], st1[r - p[len] + 1][t[len]]);
    120 }
    121 
    122 int get2(int l, int r)//查询串l和串r的LCS
    123 {//因为是翻转过来求的,所以查询要翻转一下
    124     l = n - l + 1, r = n - r + 1;
    125     l = rk[l], r = rk[r];
    126     if(l > r) swap(l, r);
    127     ++ l;
    128     int len = r - l + 1;
    129     return Min(st2[l][t[len]], st2[r - p[len] + 1][t[len]]);
    130 }
    131 
    132 void get()
    133 {
    134     int lim = n << 1;
    135     for(R k = 1; k < lim; k ++)//枚举A的长度
    136     {
    137         int maxn = 0, maxn2 = 0;
    138         for(R i = 1; i <= n; i += k)
    139         {
    140             int j = i + k;//j为下一段开头        
    141             if(j > n) break;
    142             if(i > maxn)
    143             {
    144                 int lcp = get1(i, j), lcs = get2(i, j);
    145                 maxn = i + lcp - 1; 
    146                 int l = i - lcs + 1, r = j + lcp - 2 * k;
    147                 if(lcp + lcs > k) ++ f[l], -- f[r + 1];
    148             }
    149             if(i > maxn2)
    150             {
    151                 int lcp = get2(n - i + 1, n - j + 1), lcs = get1(n - i + 1, n - j + 1);
    152                 maxn2 = i + lcp - 1; 
    153                 int l = i - lcs + 1, r = j + lcp - 2 * k;
    154                 if(lcp + lcs > k) ++ g[l], -- g[r + 1];
    155             }
    156         }    
    157     }
    158     for(R i = 1; i <= n; i ++) f[i] += f[i - 1], g[i] += g[i - 1];
    159     rev(g + 1, g + n + 1);
    160 }
    161 
    162 void work()
    163 {
    164     ans = 0;//f是开头
    165     for(R i = 1; i <= n; i += 2) 
    166         ans += 1LL * f[i] * g[i - 1] + ((i + 1 > n) ? 0 : 1LL * f[i + 1] * g[i]);
    167     printf("%lld
    ", ans);
    168 }
    169 
    170 int main()
    171 {
    172 //    freopen("in.in", "r", stdin);
    173     scanf("%d", &T);
    174     while(T --)
    175     {
    176         init();
    177         pre();
    178         get_sa();
    179         build();
    180         build1();//建st(维护LCP)
    181         rev(s + 1, s + n + 1);//翻转
    182     //    memset(rk, 0, sizeof(rk));//因为对于单组数据而言,长度不变,所以rk不必再次清空
    183         get_sa();
    184         build();
    185         build2();//建st2(维护LCS)
    186         get();
    187         work();
    188     }
    189 //    fclose(stdin);
    190     return 0;
    191 }
    View Code
  • 相关阅读:
    超级钢琴 2010年NOI
    vijos P1375 大整数(高精不熟的一定要做!)
    COGS 445. [HAOI2010]最长公共子序列
    系统升级
    mariabd mysql升级mariadb
    mysql view 视图
    mysql 杂
    mysql主从复制
    DNS迭代查询与递归查询的区别
    Python 中 str 和 repr 的区别
  • 原文地址:https://www.cnblogs.com/ww3113306/p/10067865.html
Copyright © 2020-2023  润新知