题目描述:
题目大意:有多组数据,给一个主串和n个字串,问能否用最少的字串完全覆盖主串,若能,输出子串的位置,即是第几个子串覆盖的(按照输入顺序,从1开始)。还有其在主串中的起始位置(从1开始)。若不能则输出-1。
题解:(1)首先根据贪心思想我们可以将主串的每一个位置寻找有没有对应的字串匹配,若有子串匹配则取长度最长的那个子串(在这里可以将初始长度设为-1),记录该主串位置下对应匹配子串的长度和编号。
(2)以上作法可以将主串分为许多个小区间。此时只需寻找最小数量的区间完全覆盖主串即可。对于主串第一个位置若无匹配的字串则直接输出-1。若第一个位置可以匹配,则从第一个位置起,设该区间的左端点为last,则右端点为last+mx【last】-1,对每一个主串的位置i,有它的右端点为i+mx[i]-1。只需找到与上一个区间相交或刚好相连(上一个区间的右端点+1==该区间的左端点)的右端点最大的区间即可。
(3)找到后更新last的位置。如果发现已覆盖区间的右端点==主串长度,直接结束区间覆盖过程。注意这里如果中间出现不匹配的现象,last就无法更新。之后判断已经覆盖区间的右端点是否等于主串长度,小于则输出-1。反之输出结果。
(4)以上大部分是思路,关于代码实现很多细节值得推敲,若有点懵,建议自己在纸上画画,更方便理解。
具体代码实现:
#include <bits/stdc++.h>
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define endl '\n'
#define N 205
#define fi first
#define se second
#define pb push_back
using namespace std;
const int inf=0x3f3f3f3f;
const double ex=1e-7;
const int mod=1e9+7;
int gcd(int a ,int b){ return b ? gcd(b,a%b) : a ;}
typedef pair<int,int>PII;
priority_queue<int,vector<int>,greater<int> > q;
string s;
void solve()
{
int n,num[N],mx[N],len=s.size();
string k;
cin >>n;
for(int i=0;i<=200;i++) mx[i]=-1;
for(int i=1;i<=n;i++)
{
cin >>k;
int lz=k.size();
for(int j=0;j<len;j++)
{
int t=0;
while(t<lz&&j+t<len&&k[t]==s[j+t]) t++;
if(t==lz)
{
if(lz>mx[j]) mx[j]=lz,num[j]=i;
}
}
}
if(mx[0]==-1)
{
cout <<-1<<endl;
}else{
vector<PII>v;
int last=0;
v.pb({num[0],1});
for(int i=1;i<len;i++)
{
if(i<=last+mx[last]&&i+mx[i]-1>last+mx[last]-1)
{
int index=i;
while(i<=last+mx[last]&&i<len)
{
if(i+mx[i]-1>index+mx[index]-1)
{
index=i;
}
i++;
}
i=index;
last=index;
//cout <<last<<endl;
v.pb({num[index],index+1});
if(last+mx[last]-1==len-1) break;
}
}
if(last+mx[last]-1<len-1)
{
cout <<-1<<endl;
}else{
cout <<v.size()<<endl;
for(auto it : v) cout <<it.fi<<" "<<it.se<<endl;
}
}
}
signed main()
{ios
int t;
cin >>t;
while(t--)
{
cin >>s;
solve();
}
return 0;
}