自学内容网 自学内容网

AcWing 4960.子串简写

首先就是只能得到部分测试点的做法,所谓的双重循环暴力:

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<cmath> 
#include<vector>
#include<algorithm>
#include<stack>
#include<queue>
#include<sstream>
#include<map>
#include<limits.h>
#include<set>
#define MAX 500050
#define _for(i,a,b) for(int i=a;i<(b);i++)
#define ALL(x) x.begin(),x.end()
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
LL n, m, counts, num;
char s[MAX];
int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);
    cin >> n;
    cin >> s;
    char c1, c2;
    cin >> c1 >> c2;
    int len = strlen(s);
    int res = 0;
        _for(i, 0, len) {
            _for(j, i + n-1, len) {
                if (s[i] == c1 && s[j] == c2)
                    res++;
            }
        }
        cout << res;
 
    return 0;
}

下面就是优化了。

这道题的正解应该是用的二分,一开始我也是没有想到用二分,可能之后又想了一下双指针,但是做不出来,那就讲一讲二分的做法:

我们可以首先找出结尾的字符的位置,然后再往后看以字符结尾的首。这个过程其实就是,如果我们找到了c1开头的字符,我们存储起来;如果说我们找到了c2结尾的字符,那么我们就判断这个时候我们存放的c1开头的字符有没有符合题目条件的,如果有,那么我们就得出来了在当前位置以c1为开头,以c2为结尾的所有子串。然后我们再继续向前遍历。这样的时间复杂度也就是nlogn是个很大的提升了。

那么会在代码的注释里面诠释详细的细节:

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<cmath> 
#include<vector>
#include<algorithm>
#include<stack>
#include<queue>
#include<sstream>
#include<map>
#include<limits.h>
#include<set>
#define MAX 500050
#define int long long
#define _for(i,a,b) for(int i=a;i<(b);i++)
#define ALL(x) x.begin(),x.end()
using namespace std;
string s;//字符串
vector<int>front;//用来存储开头的字符在字符串当中的位置
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);
    int k = 0;
    cin >> k;
    cin >> s;
    char c1, c2;
    cin >> c1 >> c2;
    int res = 0;//用来计数子串的个数的
    _for(i, 0, s.size()) {
        if (s[i] == c1)
            front.push_back(i);
        if (s[i] == c2) {
            if (front.empty() || i + 1 < k) {
                continue;
            }//如果说前面没有以c1开头的字符或者当前子串的长度并没有>=k,接着循环下一个结尾字符
            int left = 0;
            int right = front.size() - 1;
            while (left < right) {
                int mid=(left + right + 1) / 2;
                if (i - front[mid] + 1 >= k)
                    left = mid;//如果说这个时候满足条件的话,我们就遍历右半部分,因为可能会出现更大的数能满足条件
                else
                    right = mid - 1;
            }
            if (i - front[left] + 1 >= k)//退出循环之后看看是否存在这样的子串
                res += left + 1;//计入个数,因为我们固定了尾,遍历了可能字符的开头的位置,这些都可以成为一个子串,所以有多少个开头字符,就有多少个子串,+1是因为位置是从0开始的。
        }

    }

    cout << res;
    return 0;
}


原文地址:https://blog.csdn.net/m0_73917165/article/details/136710350

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!