Masha-forgetful(2000)dp,字符串 Codeforces Round #764 (Div. 3)

E. Masha-forgetful
time limit per test3 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
Masha meets a new friend and learns his phone number — 𝑠. She wants to remember it as soon as possible. The phone number — is a string of length 𝑚 that consists of digits from 0 to 9. The phone number may start with 0.

Masha already knows 𝑛 phone numbers (all numbers have the same length 𝑚). It will be easier for her to remember a new number if the 𝑠 is represented as segments of numbers she already knows. Each such segment must be of length at least 2, otherwise there will be too many segments and Masha will get confused.

For example, Masha needs to remember the number: 𝑠= ‘12345678’ and she already knows 𝑛=4 numbers: ‘12340219’, ‘20215601’, ‘56782022’, ‘12300678’. You can represent 𝑠 as a 3 segment: ‘1234’ of number one, ‘56’ of number two, and ‘78’ of number three. There are other ways to represent 𝑠.

Masha asks you for help, she asks you to break the string 𝑠 into segments of length 2 or more of the numbers she already knows. If there are several possible answers, print any of them.

Input
The first line of input data contains an integer 𝑡 (1≤𝑡≤104) —the number of test cases.

Before each test case there is a blank line. Then there is a line containing integers 𝑛 and 𝑚 (1≤𝑛,𝑚≤103) —the number of phone numbers that Masha knows and the number of digits in each phone number. Then follow 𝑛 line, 𝑖-th of which describes the 𝑖-th number that Masha knows. The next line contains the phone number of her new friend 𝑠.

Among the given 𝑛+1 phones, there may be duplicates (identical phones).

It is guaranteed that the sum of 𝑛⋅𝑚 (𝑛 multiplied by 𝑚) values over all input test cases does not exceed 106.

Output
You need to print the answers to 𝑡 test cases. The first line of the answer should contain one number 𝑘, corresponding to the number of segments into which you split the phone number 𝑠. Print -1 if you cannot get such a split.

If the answer is yes, then follow 𝑘 lines containing triples of numbers 𝑙,𝑟,𝑖. Such triplets mean that the next 𝑟−𝑙+1 digits of number 𝑠 are equal to a segment (substring) with boundaries [𝑙,𝑟] of the phone under number 𝑖. Both the phones and the digits in them are numbered from 1. Note that 𝑟−𝑙+1≥2 for all 𝑘 lines.

Example
inputCopy
5

4 8
12340219
20215601
56782022
12300678
12345678

2 3
134
126
123

1 4
1210
1221

4 3
251
064
859
957
054

4 7
7968636
9486033
4614224
5454197
9482268
outputCopy
3
1 4 1
5 6 2
3 4 3
-1
2
1 2 1
2 3 1
-1
3
1 3 2
5 6 3
3 4 1
Note
The example from the statement.

In the second case, it is impossible to represent by segments of known numbers of length 2 or more.

In the third case, you can get the segments ‘12’ and ‘21’ from the first phone number.

题意 :

  • 给n+1个长为m的字符串,问是否能通过前n个字符串中长度大于等于2的连续子序列组成最后一个字符串,如果不能则输出-1;如果能,则输出段数,且分别输出每一段在原字符串中的起始下标和字符串编号,任意方案即可

思路 :

  • 任何字符串都可以被若干个长为2的字符串和若干个长为3的字符串组合成(由于题目说长度大于等于2,因此不使用长为1的)
  • 在已知了所有长为2和长为3的字符串后,只要从头遍历待匹配的字符串,如果它与前一个字符能与长为2的匹配上,且上一个待匹配位置处也匹配上了,相当于这个位置可以从上一个待匹配位置处转移过来,则记录当前位置可以被匹配,且匹配方式为2个;长为3同理
  • 如何存储长为2和3的字符串,分别记录,以长度和值(纯数字字符串)为下标,结构体为元素,记录起始位置和编号
  • 如果到最后一个待匹配位置一直成功,则倒序存储方案,反之则输出-1

语法 :

  • 结构体数组也可以直接memset为0来初始化
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define endl '\n'
using namespace std;

const int N = 1e3 + 10;     // string size

int n, m;
char s[N];
struct Node
{
    int l, r, idx;
};
Node op[2][N];
bool f[N];
int tag[N];

void solve()
{
    scanf("%d%d", &n, &m);
    memset(op, 0, sizeof op);
//    memset(f, 0, sizeof f);
    
    for (int i = 1; i <= n; i ++ )
    {
        scanf("%s", s + 1);
        int x;
        
        for (int j = 1; j + 1 <= m; j ++ )
        {
            x = (s[j] - '0') * 10 + (s[j + 1] - '0');
            op[0][x] = {j, j + 1, i};
        }
        for (int j = 1; j + 2 <= m; j ++ )
        {
            x = (s[j] - '0') * 100 + (s[j + 1] - '0') * 10 + (s[j + 2] - '0');
            op[1][x] = {j, j + 2, i};
        }
    }
    
    scanf("%s", s + 1);
    for (int i = 0; i <= m; i ++ ) f[i] = false;
    f[0] = true;
    for (int i = 2; i <= m; i ++ )
    {
        int v0 = (s[i - 1] - '0') * 10 + (s[i] - '0');
        if (f[i - 2] && op[0][v0].l)
        {
            f[i] = true, tag[i] = 2;
        }
        if (i >= 3)
        {
            int v1 = (s[i - 2] - '0') * 100 + (s[i - 1] - '0') * 10 + (s[i] - '0');
            if (f[i - 3] && op[1][v1].l)
            {
                f[i] = true, tag[i] = 3;
            }
        }
    }
    
    if (f[m])
    {
        vector<Node> ve;
        for (int i = m; i; i -= tag[i])
        {
            if (tag[i] == 2)
            {
                int v = (s[i - 1] - '0') * 10 + (s[i] - '0');
                ve.push_back(op[0][v]);
            }
            else
            {
                int v = (s[i - 2] - '0') * 100 + (s[i - 1] - '0') * 10 + (s[i] - '0');
                ve.push_back(op[1][v]);
            }
        }
        reverse(ve.begin(), ve.end());
        printf("%d\n", (int)ve.size());
        for (auto item : ve) printf("%d %d %d\n", item.l, item.r, item.idx);
    }
    else printf("-1\n");
}

int main()
{
//    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    
    int _; cin >> _;
    while (_ -- )
        solve();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值