H - subsequence 2

题意

要你使用\(m\)个小写字母构造一个长度为\(n\)的字符串
\(m*(m-1)/2\)限制条件:

  • \(c_{1} 、c_{2}、 len\):表示除去其他非\(c_{1}、c_{2}\)之外的字母剩下的串长度为\(len\)
  • \(s\):除去其他非\(c_{1}、c_{2}\)之外的字母剩下的字符串,长度为\(len\)

需要我们根据这个限制条件构造出原串,如果不存在输出\(-1\)

思路

我们可以发现题目给了两个字母之间的相对位置。比如\(aab\),第二个\(a\)前面一定得接了个\(a\)\(b\)一定要第二个\(a\)的条件符合之后,然后第二个\(a\)放进去之后才能到\(b\)
那么对于每个字母都要先把前面的都输出完了,才能到自己,那么我们可以想到拓扑(其实是队友想的),一定要入度为零才能到本身,然后我们就可以和拓扑结合起来。

  • 定义字母:我们可以令\(a\)\(0000\)~\(9999\)之间的数来定义(因为最多可能有\(1e^4\)个), 然后\(b\)\(10000\)~\(19999\)这样接下去定义每个字母。
  • 建图:根据题目给的串建图, 前后建一条边。
  • 然后去跑拓扑就\(OK\)
  • 如果你构造出来的串长度不为\(n\)就输出\(-1\)(出现循环节之类乱七八糟的情况)

一些坑点

  • 建图的时候要判断会不会出现前面出现的\(a\)\(3\)个,这次出现的\(a\)\(4\)个这样的情况,这样不符合条件,可以加一个数组特判一下。
  • 输入要注意\(len = 0\)的时候会输入空行,这边要处理好。(没处理好的我,\(WA\)了好多发)

一些数据

这些数据都是输出\(-1\),其实我第一组数据之前只是输入出锅了,然后导致我一直\(WA\),其实我没有输出\(-1\)也过了

3 3
ab 3
aab
ac 2
aa
bc 0

3 3
ab 3
aab
ac 1
a
bc 1
b

AC 代码

(本人太菜,代码很乱,请多见谅)

#include<bits/stdc++.h>
#define mes(a, b) memset(a, b, sizeof a)
using namespace std;
const int maxn = 1e4+10;
int n, m;
int head[maxn*50], f[30], cnt, in[maxn*50], sum[30];
char ch[maxn];
vector<int>  ans;
struct Edge{
    int v, next;
}e[maxn*50];

void init(){    //初始化
    mes(head, -1);
    mes(in, 0);
    mes(sum, 0);
    for(int i = 0; i <= 26; i++){
        f[i] = i*10000;
    }
    ans.clear();
    cnt = 0;
}

void add(int u, int v){     
    e[++cnt].v = v;
    e[cnt].next = head[u];
    head[u] = cnt;
}

int change(int u){     //根据字母的ID转换为字母
    for(int i = 0; i < 26; i++){
        if(u - f[i] < 10000)
            return i;
    }
}

int topu(){
    queue<int> q;
    int tot = 0;
    for(int i = 0; i < m; i++){
        for(int j = 0; j < sum[i]; j++){
            if(!in[j+f[i]]){
                q.push(j+f[i]);
            }
        }
    }
    while(!q.empty()){
          int u = q.front(); q.pop();
          if(change(u) >= m)
              return -1;
          ans.push_back(change(u));
          tot++;
          for(int i = head[u]; ~i; i = e[i].next){
              int v = e[i].v;
              in[v]--;
              if(!in[v])
                  q.push(v);
          }
    }
    return tot;
}

int main(){
    scanf("%d%d", &n, &m);
    init();
    int q = m*(m-1)/2;
    char c1, c2;
    int len;
    int flag = 0;
    while(q--){
        char s[10];
        scanf("%s%d", s, &len);
        c1 = s[0];c2 = s[1];
        getchar();
        gets(ch);
        int cnt1 = 0, cnt2 = 0, u, v;       //cnt1:c1字母出现的次数
        if(len > 0){                        //cnt2:c2字母出现的次数
            if(ch[0] == c1){
                u = cnt1+f[c1-'a'];
                cnt1++;     
            }
            else{
                u = cnt2+f[c2-'a'];
                cnt2++;
            }
            for(int i = 1; i < len; i++){
                if(ch[i] == c1){
                    v = cnt1+f[c1-'a'];
                    cnt1++;
                }
                else{
                    v = cnt2+f[c2-'a'];
                    cnt2++;
                }
                add(u, v);
                 u = v;
                in[v]++;
            }
        }
        if(!sum[c1-'a']|| sum[c1-'a'] == cnt1)  //如果这次出现的次数和之前不一样,不符合条件
            sum[c1-'a'] = cnt1;
        else
            flag = 1;
        if(!sum[c2-'a']|| sum[c2-'a'] == cnt2)
            sum[c2-'a'] = cnt2;
        else
            flag = 1;
    }
    int num = topu();
    if(num != n || flag){
        printf("-1\n");
        return 0;
    }
    for(int i = 0; i < ans.size(); i++){
        printf("%c", ans[i]+'a');
    }
    printf("\n");
    return 0;
}