2024牛客暑期多校训练营7——K

题目链接

要在s串中找出子序列使其正过来和反过来的前缀同时都为t串。

首先能得出的结论就是,要让t成为前缀,那么肯定是要从正反s串中依次找到t串中每个字母的第一次出现的位置。从而符合要求的子串肯定是从这些第一次出现的位置开始往后拼接的。

前后找完之后观察,是否存在相交情况,不存在直接,计算子序列就行,如果从在通过马拉车找到包含t串末尾的回文串长度,这些长度可能就是前区间中用来交叉的长度

#include<bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define int long long
#define rep(x,a,b) for(int x=a;x<=b;x++)
#define pre(x,a,b) for(int x=a;x>=b;x--)
#define ac puts("Yes")
#define wa puts("No")
#define int long long
#define endl "\n"
#define pb push_back
#define pii pair<int, int>
#define de cout<<1;
#define mem(a,x) memset(a,x,sizeof a)
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define ull unsigned long long
#define eps 1e-6
#define RI register int
#define CI const int&
using namespace std;
const int mod=1e9+7;
const int N = 1e6 + 20;
int n, m;

vector<int> manacher(string t) {//返回 包含字符串末尾的回文串 的长度
    string s = "?#";
    for (auto ch : t) {
        s += ch;
        s += '#';
    }
    s += '!';
    vector<int> p(s.size()), len;
    int mr = 0, mid = 0, ans = 0;
    for (int i = 1; i + 1 < s.size(); i++) {
        if (i <= mr) p[i] = min(p[mid * 2 - i], mr - i + 1);
        else p[i] = 1;
        while (s[i - p[i]] == s[i + p[i]]) p[i]++;
        if (i + p[i] > mr) mr = i + p[i] - 1, mid = i;
        if (i & 1) {
            if (p[i] == 1) continue;
            int l = p[i] / 2;
            int id = (i - 1) / 2;
            if (id + l >= t.size()) len.push_back(l * 2);
        }
        else {
            int id = i / 2 - 1;
            if (id + (p[i] - 1) / 2 + 1 >= t.size()) len.push_back(p[i] - 1); 
        }
    }
    return len;
}

inline int cal(string s){//本质不同非空子序列数量
    int n=s.size();
    s=" "+s;
    vector<int>dp(n+1),lst(26);
    for(int i=1;i<=n;i++){
        dp[i]=dp[i-1];
        if(!lst[s[i]-'a'])(dp[i]+=dp[i-1]+1)%=mod;
        else (dp[i]+=(dp[i-1]-dp[lst[s[i]-'a']-1])%mod+mod)%=mod;
        lst[s[i]-'a']=i;
    }

    return dp[n];
}

void solve()
{
	cin >> n >>  m;
	string s,t;
	cin >> s >> t;
	auto len = manacher(t);
	s=' '+s;t=' '+t;
	vector<int>l,r;
	for(int i=1,j=1;i<=n;i++)
	{
		if(s[i]==t[j] && j<=m)
		{
			l.push_back(i);
			j++;
		}
	}
	for(int i=n,j=1;i>=1;i--)
	{
		if(j<=m && s[i]==t[j])
		{
			r.push_back(i);
			j++;
		}
	}

	if(l.size()<m || r.size()<m)
	{
		cout << 0 <<endl;
		return ;
	}
	int  ans=0;
	if(l[m-1] < r[m-1])//不交叉直接计算
	{
		string x="";
		rep(i,l[m-1]+1,r[m-1]-1)x+=s[i];
		ans+=cal(x)+1;//记得算是本身
	} 

	for(auto it : len)
	{
		if(it==m)
		{
			ans++;
		}else
		{
			ans+=(l[m-it-1]<r[m-1]);
		}
		 // m-w-1 在前缀中减去用来交叉的长度
        // 看剩下的前缀 l[0]->l[m-w-1] 能否与后缀拼接
	}
	cout << ans% mod<<endl;
}
signed main()
{
    IOS
	int t;
	t = 1;
	//cin >> t;
	while(t -- )
	{
		solve();
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值