poj3415 Common Substrings

11 篇文章 0 订阅
4 篇文章 0 订阅

题面在这里

题意:

求两个字符串中长度不小于k的公共子串个数。

做法:

即求sigma(两个字符串的后缀的lcp-k+1|lcp>=k)。
首先用一个没有出现过的字符拼接两个串。跑出sa和height.

两个后缀的lcp为在它们排名之间的height的最小值。
所以我们可以对于某一个height,求出它作为最小值的一个区间l[i],r[i],然后最后对于所有>=k的height统计它的贡献。
那么就可以用单调栈搞搞了。

统计答案的时候,由于只有在不同的两个串里的后缀才可以算进答案,所以我们还需预处理出pre[i]。
我们对于在第二个串里的起点都打标记1,pre[i]表示前缀和。
这样假如对于一个height[i]>=k的位子i,对答案的贡献就应当是: (sum1(l[i],i1)×sum0(i,r[i])+sum0(l[i],i1)×sum1(i,r[i]))×(height[i]k+1)
sum1(x,y)表示[x,y]中标记1的个数,sum0同理。

当然由于height[i]表示的是排名i和排名i-1的lcp,相当于像一个插空一样的存在,所以某些细节可能需要注意,诸如循环从1还是2开始,某些地方i要-1,某些地方不用-1……等等。

代码:

/*************************************************************
    Problem: poj 3415 Common Substrings
    User: bestFy
    Language: C++
    Result: Accepted
    Time: 797MS
    Memory: 7608K
    Submit_Time: 2018-01-18 17:08:19
*************************************************************/

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cctype>
#include<cmath>
using namespace std;
typedef long long ll;

const int N = 200010;
int n, m, k, len1, all, top;
char s[N], s1[N];
int rk[N], tp[N], h[N], sa[N], tong[N], st[N], l[N], r[N], pre[N];

inline void ssort() {
    for(int i = 0; i <= all; i ++) tong[i] = 0;
    for(int i = 1; i <= n; i ++) tong[rk[tp[i]]] ++;
    for(int i = 1; i <= all; i ++) tong[i] += tong[i-1];
    for(int i = n; i >= 1; i --) sa[tong[rk[tp[i]]] --] = tp[i];
}
inline void get_sa() {
    for(int i = 1; i <= n; i ++) rk[i] = s[i], tp[i] = i;
    all = 127; ssort(); int w = 1; all = 1;
    while(all < n) {
        int t = 0;
        for(int i = n-w+1; i <= n; i ++) tp[++ t] = i;
        for(int i = 1; i <= n; i ++) if(sa[i]>w) tp[++ t] = sa[i]-w;
        ssort(); for(int i = 1; i <= n; i ++) tp[i] = rk[i];
        rk[sa[1]] = all = 1;
        for(int i = 2; i <= n; i ++)
            rk[sa[i]] = (tp[sa[i]] == tp[sa[i-1]] && tp[sa[i]+w] == tp[sa[i-1]+w])?all:++ all;
        w <<= 1;
    } int k = 0;
    for(int i = 1; i <= n; i ++) {
        if(k) k --; int j = sa[rk[i]-1];
        for(; i+k<=n && j+k<=n && s[i+k] == s[j+k]; k ++);
        h[rk[i]] = k;
    }
}
int main() {
    while(~scanf("%d", &k) && k) {
        scanf("%s%s", s+1, s1+1);
        len1 = n = strlen(s+1); s[++ n] = 'z'+1;
        m = strlen(s1+1);
        for(int i = 1; i <= m; i ++) s[++ n] = s1[i];
        get_sa();
        for(int i = 1; i <= n; i ++) {
            pre[i] = pre[i-1];
            if(sa[i] <= len1) pre[i] ++;
        }
        st[top = 0] = 1;
        for(int i = 2; i <= n; i ++) {
            while(top && h[st[top]] >= h[i]) top --;
            l[i] = st[top]; st[++ top] = i;
        }
        st[top = 0] = n+1;
        for(int i = n; i >= 2; i --) {
            while(top && h[st[top]] > h[i]) top --;
            r[i] = st[top]-1; st[++ top] = i;
        }
        ll ans = 0;
        for(int i = 2; i <= n; i ++) if(h[i] >= k) {
            int lft = i-1-l[i]+1, rgt = r[i]-i+1, val = h[i]-k+1;
            ans += (ll)(pre[i-1]-pre[l[i]-1])*(rgt - (pre[r[i]]-pre[i-1]))*val;
            ans += (ll)(lft - (pre[i-1]-pre[l[i]-1]))*(pre[r[i]]-pre[i-1])*val;
        }
        printf("%lld\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值