4566: [Haoi2016]找相同字符

本文介绍了如何通过合并后缀的前缀和来解决两个字符串中寻找相同子串的问题,并提供了详细的算法实现步骤,包括后缀数组构造、高度数组计算及最终答案的获取。

4566: [Haoi2016]找相同字符

Time Limit: 20 Sec   Memory Limit: 256 MB
Submit: 536   Solved: 298
[ Submit][ Status][ Discuss]

Description

给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两
个子串中有一个位置不同。

Input

两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母

Output

输出一个整数表示答案

Sample Input

aabb
bbaa

Sample Output

10
题解:
两个串的重复子串的个数=合并后的后缀的前缀和(以两个串分别开头)
合并后的后缀的前缀和(以两个串分别开头)=合并后的后缀的前缀和-两个串单独的前缀和
具体做法详见差异
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N=400010;
int ww[N],wx[N],wy[N],wv[N];
 
bool cmp(int *r,int x,int x1,int ln)
{
    return r[x]==r[x1]&&r[x+ln]==r[x1+ln];
}
void da(int *r,int *sa,int n,int m)
{
    int *x=wx,*y=wy,*t,i,j,p;
    for(i=0;i<m;i++) ww[i]=0;
    for(i=0;i<n;i++) ww[x[i]=r[i]]++;
    for(i=1;i<m;i++) ww[i]+=ww[i-1];
    for(i=n-1;i>=0;i--) sa[--ww[x[i]]]=i;
      
    for(j=1,p=1;p<n;j*=2,m=p)
    {
        for(p=0,i=n-j;i<n;i++) y[p++]=i;
        for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
        for(i=0;i<n;i++) wv[i]=x[y[i]];
          
        for(i=0;i<m;i++) ww[i]=0;
        for(i=0;i<n;i++) ww[wv[i]]++;
        for(i=1;i<m;i++) ww[i]+=ww[i-1];
        for(i=n-1;i>=0;i--) sa[--ww[wv[i]]]=y[i];
          
        for(t=x,x=y,y=t,i=1,x[sa[0]]=0,p=1;i<n;i++)
        x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
    }
}
int rank[N],h[N];
void calheight(int *r,int *sa,int n)
{
    int i,j,k=0;
    for(i=1;i<=n;i++) rank[sa[i]]=i;
    for(i=0;i<n;h[rank[i++]]=k)
    for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
    return;
}
int q[N],t,ls[N],rs[N];
long long ans,sum;
char s1[N],s2[N];
int l1,l2,len=0;
int r[N],sa[N];
 
void get_sum()
{
    t=0,sum=0;h[0]=h[len+1]=-1;
    for(int i=1;i<=len+1;i++)
    {
        while(t&&h[q[t]]>=h[i]) rs[q[t--]]=i;
        q[++t]=i;
    }
    t=0;
    for(int i=len;i>=0;i--)
    {
        while(t&&h[q[t]]>h[i]) ls[q[t--]]=i;
        q[++t]=i;
    }
    for(int i=1;i<=len;i++)
    {
        sum+=(long long)(i-ls[i])*(rs[i]-i)*h[i]; 
    //  printf("%d",rs[i]-i);
        //printf("%d %d %d %d\n",h[i],i,ls[i],rs[i]);
    }
     
}
int main()
{
    scanf("%s%s",s1,s2);
    l1=strlen(s1),l2=strlen(s2);
    for(int i=0;i<l1;i++)
    r[len++]=s1[i]-'a'+3;
    r[len++]=1;
    da(r,sa,len+1,259);
    calheight(r,sa,len);//printf("!");
    get_sum();//
    //printf("\n");
    ans-=sum;
  
    for(int i=0;i<l2;i++)
    r[len++]=s2[i]-'a'+3;
    r[len++]=2;
    da(r,sa,len+1,259);
    calheight(r,sa,len);
    get_sum();//printf("%d\n",sum);
    ans+=sum;
    len=0;
    for(int i=0;i<l2;i++)
    r[len++]=s2[i]-'a'+3;
    da(r,sa,len+1,259);
    calheight(r,sa,len);
    get_sum();
    ans-=sum;
    printf("%lld\n",ans);
}


# P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G ## 题目背景 本题测试数据已修复。 ## 题目描述 每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 $A$ 喜欢 $B$,$B$ 喜欢 $C$,那么 $A$ 也喜欢 $C$。牛栏里共有 $N$ 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。 ## 输入格式 第一行:两个用空格分开的整数:$N$ 和 $M$。 接下来 $M$ 行:每行两个用空格分开的整数:$A$ 和 $B$,表示 $A$ 喜欢 $B$。 ## 输出格式 一行单独一个整数,表示明星奶牛的数量。 ## 输入输出样例 #1 ### 输入 #1 ``` 3 3 1 2 2 1 2 3 ``` ### 输出 #1 ``` 1 ``` ## 说明/提示 只有 $3$ 号奶牛可以做明星。 【数据范围】 对于 $10\%$ 的数据,$N\le20$,$M\le50$。 对于 $30\%$ 的数据,$N\le10^3$,$M\le2\times 10^4$。 对于 $70\%$ 的数据,$N\le5\times 10^3$,$M\le5\times 10^4$。 对于 $100\%$ 的数据,$1\le N\le10^4$,$1\le M\le5\times 10^4$。 c++,不要vector,变量名小写5字符以内,需要函数:void Tarjan(int u) { dfn[u] = low[u] = ++num; //初始化结点u的dfn和low值 st[++top] = u; //将结点u压入栈中 vis[u] = 1; //标记u在栈中 for (int i = head[u]; i; i = e[i].nxt) { //枚举u的所有出边 int v = e[i].to; if (!dfn[v]) { //结点v未被访问过,说明是树枝边 Tarjan(v); low[u] = min(low[u], low[v]); } else if (vis[v]) //v在栈中,是返祖边 low[u] = min(low[u], dfn[v]); // } int tmp = 0; if (low[u] == dfn[u]) { //结点u是该强连通分量的根 ++cnt; //强连通分量数量加一 do { //将当前结点前所有还在栈空间内的结点都归为当前强连通分量 tmp = st[top--]; vis[tmp] = 0; color[tmp] = cnt; //将同一个强连通分量内的点均标记为相同编号,也可理解为染色 } while(tmp != u); } } set<pair<int, int> > mark;//记录是否连接过 void solution() { //通过tarjan算法将所有强连通分量分配编号 for (int i = 1; i <= n; i++) if (!dfn[i]) Tarjan(i); //遍历所有连边,判断相邻两个结点是否所属同一强连通分量 for (int u = 1, v; u <= n; u++) { for (int i = head[u]; i; i = e[i].nxt) { v = e[j].to; //当相邻两个结点不属于同一强连通分量,则以强连通分量编号为点建边 if (color[u] != color[v] && mark[{color[u], color[v]}].find != mark.end()) { link(color[u], color[v]); mark.insert({color[u], color[v]}); } } } }
08-10
源码来自:https://pan.quark.cn/s/a3a3fbe70177 AppBrowser(Application属性查看器,不需要越狱! ! ! ) 不需要越狱,调用私有方法 --- 获取完整的已安装应用列表、打开和删除应用操作、应用运行时相关信息的查看。 支持iOS10.X 注意 目前AppBrowser不支持iOS11应用查看, 由于iOS11目前还处在Beta版, 系统API还没有稳定下来。 等到Private Header更新了iOS11版本,我也会进行更新。 功能 [x] 已安装的应用列表 [x] 应用的详情界面 (打开应用,删除应用,应用的相关信息展示) [x] 应用运行时信息展示(LSApplicationProxy) [ ] 定制喜欢的字段,展示在应用详情界面 介绍 所有已安装应用列表(应用icon+应用名) 为了提供思路,这里只用伪代码,具体的私有代码调用请查看: 获取应用实例: 获取应用名和应用的icon: 应用列表界面展示: 应用列表 应用运行时详情 打开应用: 卸载应用: 获取info.plist文件: 应用运行时详情界面展示: 应用运行时详情 右上角,从左往右第一个按钮用来打开应用;第二个按钮用来卸载这个应用 INFO按钮用来解析并显示出对应的LSApplicationProxy类 树形展示LSApplicationProxy类 通过算法,将LSApplicationProxy类,转换成了字典。 转换规则是:属性名为key,属性值为value,如果value是一个可解析的类(除了NSString,NSNumber...等等)或者是个数组或字典,则继续递归解析。 并且会到superClass的属性并解析,superClass如...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值