zoukankan      html  css  js  c++  java
  • 洛谷P4112 最短不公共子串

    题意:

    下面,给两个小写字母串A,B,请你计算:

    (1) A的一个最短的子串,它不是B的子串

    (2) A的一个最短的子串,它不是B的子序列

    (3) A的一个最短的子序列,它不是B的子串

    (4) A的一个最短的子序列,它不是B的子序列

    解:这是什么四合一毒瘤题......

    先上正解:

    第一问对B建后缀自动机,枚举A的每个前缀,在B上跑。

    第二问枚举A中子串的开始位置,B中贪心匹配。O(n2)。

    后两问分别建出B的后缀自动机和序列自动机,然后DP,用f[i][j]表示考虑A的前i个字符,在自动机上走到节点j至少需要几步。答案是f[n][null]

    转移就是看节点j有没有A[i + 1]的出边,更新。

    好的,接下来上我的SB解法......

    第一问,广义后缀自动机裸题。

    第二问,我们只需要找出A中的一个子串,它向前/后添加一个字符时在B中无法继续匹配即可。

    对A建后缀自动机。

    由于这样后缀自动机一个节点的多个子串是逐渐在前面添加字符,所以考虑从后往前找子串,在B中也从后往前匹配。

    每个节点有一个posA表示该节点代表的的串的结尾在A中的位置,还有一个pos表示该节点代表的最长串在在B中从后向前贪心匹配到的最右距离。

    在fail树上DFS。每个点继承它父亲的pos并一个一个贪心匹配直到这个点代表的最长串完成匹配。如果失配了就更新答案。否则就记录pos,向下DFS。

    第三问,首先可以得知,如果A中的每一个字符都在B中出现过,那么符合条件的A一定是B的一个子串加上一个B中没有的转移。

    于是建出B的后缀自动机。

    我们用pos[x]表示节点x所表示的最短子串在A中从前往后贪心匹配到的最短距离。为什么是最短呢?因为我们要越短越好 + 越靠左越好。因为这个节点的每个子串加上一个不存在的转移都会合法,所以短比长优。而靠左的更可能在A中找到B不存在的转移。

    而不存在一个非最短子串的最靠左距离会左于最短子串。

    于是我们分析了一大通之后发现,只要按照拓扑序更新pos,取最小值就行了。

    在每个节点的pos[x]+1开始向右扫A,如果没有转移就更新答案。否则更新下一个节点的pos。

    第四问实在是想不出来了,就跑去看题解,学习了一波序列自动机,用了正解写的。

      1 #include <cstdio>
      2 #include <algorithm>
      3 #include <cstring>
      4 #include <queue>
      5 
      6 const int N = 8010;
      7 
      8 struct Edge {
      9     int nex, v;
     10 }edge[N << 1]; int top;
     11 
     12 int tr[N * 2][26], fail[N], len[N], tot, cnt[N], bin[N], topo[N], last;
     13 int e[N], n, m;
     14 char A[N], B[N];
     15 
     16 inline void add(int x, int y) {
     17     top++;
     18     edge[top].v = y;
     19     edge[top].nex = e[x];
     20     e[x] = top;
     21     return;
     22 }
     23 
     24 inline void insert(char c) {
     25     int f = c - 'a';
     26     int p = last, np = ++tot;
     27     last = np;
     28     len[np] = len[p] + 1;
     29     while(p && !tr[p][f]) {
     30         tr[p][f] = np;
     31         p = fail[p];
     32     }
     33     if(!p) {
     34         fail[np] = 1;
     35     }
     36     else {
     37         int Q = tr[p][f];
     38         if(len[Q] == len[p] + 1) {
     39             fail[np] = Q;
     40         }
     41         else {
     42             int nQ = ++tot;
     43             len[nQ] = len[p] + 1;
     44             fail[nQ] = fail[Q];
     45             fail[Q] = fail[np] = nQ;
     46             memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
     47             while(tr[p][f] == Q) {
     48                 tr[p][f] = nQ;
     49                 p = fail[p];
     50             }
     51         }
     52     }
     53     return;
     54 }
     55 
     56 inline void clear() {
     57     memset(tr, 0, sizeof(tr));
     58     memset(fail, 0, sizeof(fail));
     59     memset(e, 0, sizeof(e));
     60     memset(len, 0, sizeof(len));
     61     memset(bin, 0, sizeof(bin));
     62     memset(topo, 0, sizeof(topo));
     63     memset(cnt, 0, sizeof(cnt));
     64     last = tot = top = 0;
     65     return;
     66 }
     67 
     68 namespace t1 {
     69     bool vis[N];
     70     inline int split(int p, int f) {
     71         int Q = tr[p][f], nQ = ++tot;
     72         len[nQ] = len[p] + 1;
     73         fail[nQ] = fail[Q];
     74         fail[Q] = nQ;
     75         memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
     76         while(tr[p][f] == Q) {
     77             tr[p][f] = nQ;
     78             p = fail[p];
     79         }
     80         return nQ;
     81     }
     82     inline int insert(char c, int p) {
     83         int f = c - 'a';
     84         if(tr[p][f]) {
     85             int Q = tr[p][f];
     86             if(len[Q] == len[p] + 1) {
     87                 return Q;
     88             }
     89             return split(p, f);
     90         }
     91         int np = ++tot;
     92         len[np] = len[p] + 1;
     93         while(p && !tr[p][f]) {
     94             tr[p][f] = np;
     95             p = fail[p];
     96         }
     97         if(!p) {
     98             fail[np] = 1;
     99         }
    100         else {
    101             int Q = tr[p][f];
    102             if(len[Q] == len[p] + 1) {
    103                 fail[np] = Q;
    104             }
    105             else {
    106                 fail[np] = split(p, f);
    107             }
    108         }
    109         return np;
    110     }
    111     void DFS(int x) {
    112         if(vis[x] || !x) {
    113             return;
    114         }
    115         vis[x] = 1;
    116         DFS(fail[x]);
    117         return;
    118     }
    119     inline void solve() {
    120         int last = 1;
    121         tot = 1;
    122         for(int i = 0; i < n; i++) {
    123             last = insert(A[i], last);
    124         }
    125         last = 1;
    126         for(int i = 0; i < m; i++) {
    127             last = insert(B[i], last);
    128         }
    129         // SAM over
    130         int p = 1;
    131         for(int i = 0; i < m; i++) {
    132             p = tr[p][B[i] - 'a'];
    133             DFS(p);
    134         }
    135         int ans = n + m;
    136         for(int i = 2; i <= tot; i++) {
    137             if(!vis[i]) {
    138                 ans = std::min(ans, len[fail[i]] + 1);
    139             }
    140         }
    141         printf("%d
    ", (ans != n + m) ? ans : -1);
    142         return;
    143     }
    144 }
    145 
    146 namespace t2 {
    147     // pos[i] Node i's last position in B
    148     int pos[N], posA[N], ans;
    149     std::queue<int> Q;
    150     void DFS(int x) {
    151         //printf("x = %d posA[x] = %d 
    ", x, posA[x]);
    152         int p = pos[fail[x]] - 1;
    153         for(int i = posA[x] - len[fail[x]]; i >= posA[x] - len[x] + 1; i--) {
    154             //printf(" > i = %d 
    ", i);
    155             while(p >= 0 && B[p] != A[i]) {
    156                 //printf(" > > p = %d 
    ", p);
    157                 p--;
    158             }
    159             //printf(" > > p = %d 
    ", p);
    160             if(p < 0) {
    161                 ans = std::min(ans, posA[x] - i + 1);
    162                 return;
    163             }
    164             else {
    165                 pos[x] = p;
    166                 p--;
    167             }
    168         }
    169         for(int i = e[x]; i; i = edge[i].nex) {
    170             int y = edge[i].v;
    171             DFS(y);
    172         }
    173         return;
    174     }
    175     inline void solve() {
    176         clear();
    177         last = tot = 1;
    178         for(int i = 0; i < n; i++) {
    179             int t = tot;
    180             insert(A[i]);
    181             for(int j = t + 1; j <= tot; j++) {
    182                 posA[j] = i;
    183             }
    184         }
    185         ans = n + m;
    186         for(int i = 2; i <= tot; i++) {
    187             add(fail[i], i);
    188             //printf("add %d %d 
    ", fail[i], i);
    189         }
    190         pos[1] = m;
    191         DFS(1);
    192         printf("%d
    ", ans != n + m ? ans : -1);
    193         return;
    194     }
    195 }
    196 
    197 namespace t3 {
    198     int pos[N], ans;
    199     inline void solve() {
    200         for(int i = 0; i < m; i++) {
    201             bin[B[i]]++;
    202         }
    203         for(int i = 0; i < n; i++) {
    204             if(!bin[A[i]]) {
    205                 printf("1
    ");
    206                 return;
    207             }
    208         }
    209         clear();
    210         tot = last = 1;
    211         for(int i = 0; i < m; i++) {
    212             insert(B[i]);
    213         }
    214         for(int i = 1; i <= tot; i++) {
    215             bin[len[i]]++;
    216         }
    217         for(int i = 1; i <= tot; i++) {
    218             bin[i] += bin[i - 1];
    219         }
    220         for(int i = 1; i <= tot; i++) {
    221             topo[bin[len[i]]--] = i;
    222         }
    223         //
    224         memset(pos, 0x3f, sizeof(pos));
    225         ans = n + m;
    226         pos[1] = -1;
    227         for(int a = 1; a <= tot; a++) {
    228             int x = topo[a];
    229             for(int i = pos[x] + 1; i < n; i++) {
    230                 int f = A[i] - 'a';
    231                 if(!tr[x][f]) {
    232                     ans = std::min(ans, len[fail[x]] + 2);
    233                 }
    234                 else {
    235                     pos[tr[x][f]] = std::min(pos[tr[x][f]], i);
    236                 }
    237             }
    238         }
    239         printf("%d
    ", ans != n + m ? ans : -1);
    240         return;
    241     }
    242 }
    243 
    244 namespace t4 {
    245     int nex[N][26], last[26], f[2010][2010];
    246     inline void exmin(int &a, int b) {
    247         a > b ? a = b : 0;
    248         return;
    249     }
    250     inline void solve() {
    251         clear();
    252         memset(f, 0x3f, sizeof(f));
    253         for(int i = 0; i < 26; i++) {
    254             last[i] = m + 2;
    255         }
    256         for(int i = m - 1; i >= 0; i--) {
    257             for(int j = 0; j < 26; j++) {
    258                 nex[i + 2][j] = last[j];
    259             }
    260             last[B[i] - 'a'] = i + 2;
    261         }
    262         for(int j = 0; j < 26; j++) {
    263             nex[1][j] = last[j];
    264         }
    265         f[0][1] = 0;
    266         for(int i = 0; i < n; i++) {
    267             for(int j = 1; j <= m + 2; j++) {
    268                 exmin(f[i + 1][j], f[i][j]);
    269                 int k = A[i] - 'a';
    270                 //printf("i %d j %d  nex[j][k] %d 
    ", i, j, nex[j][k]);
    271                 // tr[j][f]
    272                 if(!nex[j][k]) {
    273                     continue;
    274                 }
    275                 exmin(f[i + 1][nex[j][k]], f[i][j] + 1);
    276                 //printf("f %d %d -> f %d %d : %d 
    ", i, j, i + 1, nex[j][k], f[i][j] + 1);
    277             }
    278         }
    279         printf("%d
    ", f[n][m + 2] == f[n][m + 3] ? -1 : f[n][m + 2]);
    280         return;
    281     }
    282 }
    283 
    284 int main() {
    285     scanf("%s%s", A, B);
    286     n = strlen(A);
    287     m = strlen(B);
    288     t1::solve();
    289     t2::solve();
    290     t3::solve();
    291     t4::solve();
    292 
    293     return 0;
    294 }
    AC代码

    附:序列自动机。

    对于长为n的序列,序列自动机有n + 1个节点,分别表示这n个位置和起点。每个节点都有字符集个转移边,指向该字符在它之后第一次出现的位置。fail指针指向上一个它出现的位置。

    能够识别所有的子序列。

  • 相关阅读:
    Qt图片显示
    C# 对话框使用整理
    C# AutoResetEvent 使用整理
    C++ 模板
    superset使用
    superset部署
    kafka修改topic副本数
    c# 生成自定义图片
    c# 解决Randoms伪随机重复问题
    c# 图片加水印
  • 原文地址:https://www.cnblogs.com/huyufeifei/p/10383732.html
Copyright © 2011-2022 走看看