【数据结构】线性处理字符串中指定字串的个数问题

本文介绍了一个字符串处理问题,给定两个字符串S和T,目标是计算最多能在S中删除多少次T的子序列。通过动态规划算法,可以将时间复杂度降低到线性。代码示例展示了如何利用动态规划状态转移实现这一算法,通过cnt数组记录T的前缀子串在S中出现的次数,最终输出cnt[m]即为答案。
摘要由CSDN通过智能技术生成

一、问题引入

给出一个长度为n的字符串S和一个长度为m的不含重复字符的字符串T,每次你可以在S中删除一个等于T的子序列,最多可以删除多少次?
如字符串S=ababccd,T=abc,可以选择s1s2s5,s1s2s6,s1s4s5,s1s4s6,s3s4s5,s3s4s6进行删除,删除后分别得到abcd,abcd,bacd,bacd,abcd,abcd。
如果删除后得到abcd,则还可以再进行一次删除,最多可以删除2次。
这里注意,字符串T种只会包含小写字母以及T中不会出现重复的字符

二、问题分析

提炼一下本题的意思,我们可以把本题精炼为求字符串S的字串中,在每个字符不能重复出现的条件下的子串中字符串T的个数。比如在上文的例子中,ababccd中的abc就只有2个,因此答案为2。

三、解法分析

1.暴力求解

如果我们通过暴力求解的思路来求解这个问题的话,我们需要枚举S中的每一个字符作为起点,之后如果与T中的第一个字符匹配了就再去匹配T中的下一个字符…时间复杂度为O(NM)这显然不是一个合适的算法。

2.线性求解

此解法实际上使用到了动态规划的思路来解决问题,如果在阅读本篇博客之前从未接触过动态规划算法则会比较难理解本题代码的思路。在这里先列出含有注释的代码,在后文中再结合代码来进行算法的分析。

#include <bits/stdc++.h>
#define endl '\n'
#define int long long
#define INF 0x3f3f3f3f
#define ull unsigned long long
#define mem(a, b) memset(a, b, sizeof(a))
#define ck(x) cerr << #x << "=" << x << '\n';
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 7, mod = 1e9 + 7;

void solve()
{
    int n, m;
    cin >> n >> m;//n和m分别是s和t的长度
    string s, t;
    cin >> s >> t;
    vector<int> cnt(m + 5), pos(26, -1);
    //vector动态开辟数组,cnt[m+5],pos[26](pos数组中的值全赋值为-1)
    //pos数组大小为26,用来记录字符串T中26个英文字母是否在T中出现,若出现,记录下位置
    for (int i = 0; i < m; i++)
    {
      pos[t[i] - 'a'] = i + 1;//用来记录每个字符在T中的位置
    }
    cnt[0] = n;//动态规划初始化
    for (int i = 0; i < n; i++)//从左往右遍历S数组
    {
      char c = s[i];
      int x = pos[c - 'a'];//记录一下S中第i个字符c在T中的位置
      if (x != -1 && cnt[x - 1] > 0)//如果这个字符c在T中存在并且c的前一个字符已经出现过了
      {
      	//匹配成功,状态转移
        cnt[x - 1] -= 1;
        cnt[x] += 1;
      }
    }
    cout << cnt[m] << "\n";//输出最后匹配到的状态
}

signed main()
{
  std::ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  solve();
  return 0;
}

光看代码可能比较难以理解,我们先结合一个例子来看看:
S=“ababcc”
T=“abc”
cnt[4]={6,0,0,0}
pos[26]={1,2,3,-1,-1,-1…}

#1:S[0]=a,x=1
cnt[4]={5,1,0,0}:“a”
#2:S[0]=b,x=2
cnt[4]={5,0,1,0}:“ab”
#3:S[0]=a,x=1
cnt[4]={4,1,1,0}:“ab”“a”
#4:S[0]=b,x=2
cnt[4]={4,0,2,0}:“ab”“ab”
#5:S[0]=c,x=3
cnt[4]={4,0,1,1}:“abc”“ab”
#6:S[0]=c,x=3
cnt[4]={4,0,0,2}:“abc”“abc”
最后得出匹配到的字串T为2个

通过样例我们发现,cnt[i]的含义是用来动态存储枚举过程中的,由T的前i个字符组成的子串,出现的个数,当我们枚举完S后,cnt[m]就是我们要求的答案。

通过动态规划的思路,我们可以极大的简化时间复杂度,但是理解上就会稍微复杂一些。关键在于cnt数组的意义理解和对于动态规划的状态转移的理解。

作者:Avalon·Demerzel

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值