zoukankan      html  css  js  c++  java
  • 洛谷P2375 动物园

    我要死了。这是我做过的最恶心的题之一。

    天下第一的大毒瘤。有gay毒。

    我不如熊猫好多年...


    题意:给定字符串,求g[i],表示:[0, i]中满足该子串既是前缀又是后缀还不重叠的子串数。

    解:题面都写了KMP,想必跟KMP有关。

    然后我痛苦的思考了1天无果......

    我首先想到求出nex[]和f[],f[]表示可重叠的既是前缀又是后缀的子串数。

    然后想找出f[]和g[]的关系,失败。

    然后想到再来一个len[]表示f[]最长的那一个的长度,还是不行...

    被折磨了一天,撑不住了,在吃完饭的时候开始看题解。

    发现:我们要魔改KMP,每次跳完增加之后若大于一半,就再跳...

    这时你停住的地方j就是恰好比一半小的最大值。

    然后如果i这里的nex为0,j显然为0,g[]显然也为0

    否则j不为0,这时你把f[j - 1]加1就是g[i]

    你加的1就是[0, j - 1]这个串。

    然后j可以直接继承到下一个i

    因为你的j和KMP里的j差不多,除了不超过一半之外都一样。

    然后你i每次 + 1的时候g[]最多 + 1

    这样就搞过了这个大毒瘤题....

    (事实上还不是很清楚)

     1 #include <cstdio>
     2 #include <cstring>
     3 
     4 typedef long long LL;
     5 
     6 const int N = 1000010, MO = 1e9 + 7;
     7 
     8 int nex[N], f[N], g[N];
     9 char s[N];
    10 
    11 inline void solve() {
    12     scanf("%s", s);
    13     int n = strlen(s);
    14     nex[0] = 0;
    15     f[0] = 0;
    16     for(int i = 1, j = 0; i < n; i++) {
    17         while(j && s[i] != s[j]) {
    18             j = nex[j - 1];
    19         }
    20         if(s[i] == s[j]) {
    21             j++;
    22         }
    23         nex[i] = j;
    24         if(j) {
    25             f[i] = f[j - 1] + 1;
    26         }
    27         else {
    28             f[i] = 0;
    29         }
    30     }
    31 
    32     g[0] = 0;
    33     LL ans = 1;
    34     for(int i = 1, j = 0; i < n; i++) {
    35         while(j && s[i] != s[j]) {
    36             j = nex[j - 1];
    37         }
    38         if(s[i] == s[j]) {
    39             j++;
    40         }
    41         while(2 * j > (i + 1)) {
    42             j = nex[j - 1];
    43         }
    44         if(!j) {
    45             g[i] = 0;
    46         }
    47         else {
    48             g[i] = f[j - 1] + 1;
    49         }
    50         ans = (ans * (g[i] + 1)) % MO;
    51     }
    52     printf("%lld
    ", ans);
    53     return;
    54 }
    55 
    56 int main() {
    57     int T;
    58     scanf("%d", &T);
    59     while(T--) {
    60         solve();
    61     }
    62     return 0;
    63 }
    AC代码

    [update]首先有个30分暴力想必大家都会。

    说一下SAM做法。你考虑一个border是什么样的,显然前缀是一条链,而后缀是该点在fail树上到根的链。于是相当于求这两个东西的交,还有个深度限制。

    给前缀链标记为1,非前缀标记为0,并统计到根路径的点权和。这样,我们只要找到每个点在fail树上满足限制的最深的点,就能知道答案了。

    考虑到每个点在fail树上向下走的时候,满足限制的最深点也是单调向下的。于是拿一个栈维护当前点到根的链,用一个单调指针在栈上找就行了。

      1 #include <bits/stdc++.h>
      2 
      3 #define add(x, y) edge[++tp].v = y; edge[tp].nex = e[x]; e[x] = tp
      4 
      5 const int N = 2000010, MO = 1e9 + 7;
      6 
      7 struct Edge {
      8     int nex, v;
      9 }edge[N]; int tp;
     10 
     11 int n, tr[N][26], fail[N], len[N], e[N], vis[N], Time, val[N], stk[N], top, ans, tot = 1, last = 1;
     12 char str[N];
     13 std::queue<int> Q;
     14 /*
     15 2
     16 abcababc
     17 abcababc
     18 */
     19 namespace bf {
     20     typedef unsigned long long uLL;
     21     const uLL B = 131;
     22     uLL pw[300], h[300];
     23     inline void prework() {
     24         pw[0] = 1;
     25         for(int i = 1; i <= n; i++) {
     26             pw[i] = pw[i - 1] * B;
     27             h[i] = h[i - 1] * B + str[i];
     28         }
     29         return;
     30     }
     31     inline uLL Hash(int l, int r) {
     32         return h[r] - h[l - 1] * pw[r - l + 1];
     33     }
     34     inline void solve() {
     35         prework();
     36         int ans = 1;
     37         for(int i = 1; i <= n; i++) {
     38             /// [1, i]
     39             //printf("i = %d 
    ", i);
     40             int temp = 1;
     41             for(int t = i >> 1; t; t--) {
     42                 //printf("t = %d 
    ", t);
     43                 if(Hash(1, t) == Hash(i - t + 1, i)) {
     44                     /// num[i] = t;
     45                     ++temp;
     46                 }
     47             }
     48             ans = 1ll * ans * temp % MO;
     49         }
     50         printf("%d
    ", ans);
     51         return;
     52     }
     53 }
     54 
     55 inline void insert(int f) {
     56     int p = last, np = ++tot;
     57     last = np;
     58     len[np] = len[p] + 1;
     59     vis[np] = Time;
     60     while(p && !tr[p][f]) {
     61         tr[p][f] = np;
     62         p = fail[p];
     63     }
     64     if(!p) {
     65         fail[np] = 1;
     66     }
     67     else {
     68         int Q = tr[p][f];
     69         if(len[Q] == len[p] + 1) {
     70             fail[np] = Q;
     71         }
     72         else {
     73             int nQ = ++tot;
     74             len[nQ] = len[p] + 1;
     75             fail[nQ] = fail[Q];
     76             fail[Q] = fail[np] = nQ;
     77             memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
     78             while(tr[p][f] == Q) {
     79                 tr[p][f] = nQ;
     80                 p = fail[p];
     81             }
     82         }
     83     }
     84     return;
     85 }
     86 
     87 inline void clear() {
     88     for(register int i(1); i <= tot; i++) {
     89         memset(tr[i], 0, sizeof(tr[i]));
     90         val[i] = e[i] = fail[i] = len[i] = 0;
     91     }
     92     last = tot = 1;
     93     tp = 0;
     94     return;
     95 }
     96 
     97 void DFS(int x, int p) {
     98     stk[++top] = x;
     99     /// calc ans x
    100     //printf("x = %d 
    ", x);
    101     while(p < top && len[stk[p + 1]] * 2 <= len[x]) {
    102         ++p;
    103     }
    104     if(vis[x] == Time) {
    105         ans = 1ll * ans * (val[stk[p]] + 1) % MO;
    106     }
    107     for(int i = e[x]; i; i = edge[i].nex) {
    108         int y = edge[i].v;
    109         val[y] = val[x] + (vis[y] == Time);
    110         DFS(y, p);
    111     }
    112     --top;
    113     return;
    114 }
    115 
    116 inline void solve() {
    117     scanf("%s", str + 1);
    118     n = strlen(str + 1);
    119     /*if(n <= 200) {
    120         bf::solve();
    121         return;
    122     }*/
    123     ++Time;
    124     ans = 1;
    125     for(register int i(1); i <= n; i++) {
    126         insert(str[i] - 'a');
    127     }
    128     for(register int i(2); i <= tot; i++) {
    129         add(fail[i], i);
    130     }
    131 
    132     DFS(1, 1); /// get val
    133     printf("%d
    ", ans);
    134     clear();
    135     return;
    136 }
    137 
    138 int main() {
    139     int T;
    140     scanf("%d", &T);
    141     while(T--) {
    142         solve();
    143         if(T) memset(str + 1, 0, n * sizeof(char));
    144     }
    145     return 0;
    146 }
    AC代码

    这题非常良心的没卡空间。

  • 相关阅读:
    Java基础--(一)hello world
    性能测试--jmeter安装与配置
    性能测试--(四)函数
    性能测试--(三)jmeter参数化
    (一)Monkey使用场景及常用命令
    (二)logcat/trace.txt日志文件的分析
    (一)adb部署及使用
    SOAP UI破解及安装
    性能测试常用指标
    shll 基础讲解
  • 原文地址:https://www.cnblogs.com/huyufeifei/p/9691072.html
Copyright © 2011-2022 走看看