[CF235C] Cyclical Quest

本文深入探讨了SAM算法的应用,特别是如何解决字符串匹配问题。通过实例解释了如何构建SAM自动机,并详细说明了如何处理循环同构子串的查询,强调了正确设置root节点的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

给一个长度为n 的字符串s,每次询问一个串q 问s 有多少个子串与q 循环同构。
s长度,q总长度小于10^6。

分析

就是把q复制一遍,然后再sam上面跑,如果循环了,就不加入答案。
好久没打SAM…需要复习一下。
卡了好久,因为有几句话打反了。
为了程序不出错,最好把root设置成1。
跑的时候注意,用mat记录匹配的字符串长度,注意一定要跑到完全代表s[i~i+len(q)]的点,不然答案会小。

代码

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<set>
#include<bitset>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
typedef long long ll;
typedef double db;
const int N=2e6+5,mo=1e8;//7707253;
int tr[N][26],fail[N],mx[N],rt,kmp[N],lst[N],n,len,i,j,k,ts,q1,q2,d[N],x,tmp,l,mat,vis[N];
char s[N];
ll ans,cnt[N];
int tt,b[N],first[N],next[N];
void cr(int x,int y)
{
    tt++;
    b[tt]=y;
    next[tt]=first[x];
    first[x]=tt;
}
int ins(int lst,int x)
{
    int np=++ts,p=lst,nq,q;
    mx[np]=mx[p]+1;
    cnt[np]=1;
    while (p&&!tr[p][x]) tr[p][x]=np,p=fail[p];
    if (!p) fail[np]=1;else
    {
        q=tr[p][x];
        if (mx[q]==mx[p]+1) fail[np]=q;else
        {
            nq=++ts;
            memcpy(tr[nq],tr[q],sizeof(tr[q]));
            mx[nq]=mx[p]+1;
            while (p&&tr[p][x]==q) tr[p][x]=nq,p=fail[p];
            fail[nq]=fail[q];
            fail[q]=fail[np]=nq;
        }
    }
    return np;
}
int main()
{
    freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    scanf("%s\n",s+1);
    len=strlen(s+1);
    //root=1;
    ts=1;
    fail[1]=0;
    lst[0]=1;
    fo(i,1,len) lst[i]=ins(lst[i-1],s[i]-'a');
    fo(i,2,ts) cr(fail[i],i);
    q1=0;d[q2=1]=1;
    while(q1<q2) for(int p=first[d[++q1]];p;p=next[p]) d[++q2]=b[p];
    fd(i,ts,1) cnt[fail[d[i]]]+=cnt[d[i]];
    scanf("%d\n",&n);
    fo(l,1,n)
    {
        scanf("%s\n",s+1);
        len=strlen(s+1);
        fo(i,1,len) s[i+len]=s[i];
        ans=0;tmp=1;mat=0;
        fo(i,1,len*2)
        {
            while (tmp&&!tr[tmp][s[i]-'a']) tmp=fail[tmp],mat=mx[tmp];
            if (!tmp) tmp=1,mat=0;
            else tmp=tr[tmp][s[i]-'a'],mat++;
            while (tmp&&mx[fail[tmp]]>=len) tmp=fail[tmp],len=mx[tmp];
            if (mat>=len&&vis[tmp]!=l)  vis[tmp]=l,ans+=cnt[tmp];
        }
        printf("%I64d\n",ans);
    }
}
<think>首先,用户的问题是关于SIP协议中的OPTIONS请求和响应的交互内容分析,特别是Via、From、To、Call-ID、CSeq字段的解析。用户引用了几个参考内容,我需要基于这些引用来构建回答。 关键引用总结: - 引用[1]:SIP请求必须包含的头域:To、From、CSeq、Call-ID、MaxForwards、Via。这些是核心路由信息。 - 引用[2]:Via头字段的作用:记录请求的路由,帮助响应返回。代理添加Via字段,响应时复制并删除。 - 引用[3]:CSeq头域的作用:用于序列化请求,区分不同请求,避免混淆。 - 引用[4]:一个OPTIONS响应的实例,展示了字段如Via、To、From、Call-ID、CSeq。 - 引用[5]:SIP请求和响应都有的头域,包括协议细节。 用户要求分析SIP OPTIONS请求和响应的交互内容,聚焦于Via、From、To、Call-ID、CSeq字段的解析。 我的回答需要: - 使用中文。 - 结构清晰,逐步解决问题。 - 确保回答真实可靠,基于提供的引用。 - 在行内数学表达式使用$...$,但这里可能不需要数学表达式,因为主题是SIP协议。 - 独立公式使用$$...$$,但同样,这里可能不需要。 - 生成相关问题。 - 在引用的段落末尾添加引用标识,例如[^1]。 回答结构: 1. **介绍SIP OPTIONS请求**:简要说明OPTIONS请求的作用,例如查询服务器能力。 2. **解析请求中的字段**:详细解释Via、From、To、Call-ID、CSeq在OPTIONS请求中的作用和格式。 3. **解析响应中的字段**:同样解释这些字段在响应中的含义,基于引用[4]的实例。 4. **交互过程**:描述请求和响应如何通过这些字段交互,路由返回。 5. **生成相关问题**:在末尾添加相关问题。 确保引用正确: - 在回答中,当提到引用内容时,自然地添加引用标识。 例如: - 根据引用[1],SIP请求必须包含... [^1] - Via字段用于... [^2] 现在,构建内容: - **SIP OPTIONS请求**:用于查询服务器支持的方法和能力。请求行包含方法、RequestURI、SIP版本。 - **字段解析**: - **Via**:记录请求路径。请求时,UA添加自己的地址;代理添加在顶部。响应时,按顺序复制并路由回。[^2] - **From**:发起请求的UA地址。[^1] - **To**:目标UA地址。[^1] - **Call-ID**:唯一标识一个对话或会话。[^1] - **CSeq**:序列号,用于排序请求和响应,区分不同事务。[^3] - **响应实例分析**:基于引用[4],展示字段值并解释。 - **交互**:请求发送时,Via堆栈建立;响应返回时,代理使用Via堆栈路由。 相关问题: 1. SIP OPTIONS请求的常见应用场景是什么? 2. 如何确保SIP消息的路由正确性? 3. CSeq字段在SIP事务中的作用是什么? 最后,确保回答完整、专业。</think>### SIP OPTIONS请求响应分析 SIP(Session Initiation Protocol)协议用于多媒体会话的建立、修改和终止。OPTIONS请求是一种查询方法,用于询问服务器或用户代理(UA)支持的能力和方法,而不建立会话。例如,UA可以发送OPTIONS请求来查询对方支持的编解码器或SIP方法。请求和响应的交互依赖于核心头域进行路由和事务管理。下面,我将逐步解析OPTIONS请求和响应中的关键字段:Via、From、To、Call-ID、CSeq。这些字段在所有SIP请求中必须包含,提供路由、事务标识和序列控制的核心信息[^1]。 #### 1. **OPTIONS请求的字段解析** 当UA发送OPTIONS请求时,请求行指定方法(OPTIONS)、RequestURI(目标地址)和SIP版本(如SIP/2.0)。之后,头域提供详细信息: - **Via**:记录请求的传输路径,帮助响应路由回发送方。请求发起时,UA在Via中添加自己的地址(包括协议、版本、传输方式如UDP/TCP、端口和分支参数)。代理转发时,在Via列表顶部添加自己的地址。分支参数(branch)是Request-URI、To、From、Call-ID和CSeq的哈希值,用于唯一标识事务[^2]。例如: ``` Via: SIP/2.0/UDP client.example.com:5060;branch=z9hG4bK74gh5 ``` 这表示请求通过UDP从client.example.com的5060端口发出,分支值用于防重复。 - **From**:标识请求的发起方,包含URI和可选标签(tag)。标签用于区分同一Call-ID下的多个事务[^1]。例如: ``` From: "Alice" <sip:alice@example.com>;tag=1234 ``` 这表示请求由Alice发起,标签1234用于后续响应匹配。 - **To**:标识请求的目标方,格式类似From。在请求中,To可能不包含标签,直到响应中添加[^1]。例如: ``` To: <sip:bob@example.com> ``` 目标服务器在响应中会添加标签以完成对话。 - **Call-ID**:全局唯一标识符,用于关联同一会话或对话中的所有消息。通常基于时间戳和随机数生成,确保不同请求的Call-ID不重复[^1][^3]。例如: ``` Call-ID: a1b2c3d4@client.example.com ``` 这标识了整个交互的事务链。 - **CSeq**:序列号,由方法名(如OPTIONS)和数字组成,用于排序请求和响应,防止混淆(如区分INVITE和CANCEL)。数字递增确保事务顺序[^3]。例如: ``` CSeq: 101 OPTIONS ``` 这表示这是该Call-ID下的第101个请求,方法为OPTIONS。 完整的OPTIONS请求示例: ``` OPTIONS sip:bob@example.com SIP/2.0 Via: SIP/2.0/UDP client.example.com:5060;branch=z9hG4bK74gh5 From: "Alice" <sip:alice@example.com>;tag=1234 To: <sip:bob@example.com> Call-ID: a1b2c3d4@client.example.com CSeq: 101 OPTIONS Max-Forwards: 70 Contact: <sip:alice@client.example.com> Content-Length: 0 ``` #### 2. **OPTIONS响应的字段解析** 服务器收到OPTIONS请求后,返回响应(如200 OK),包含相同头域但值可能更新。响应复制请求的Via、From、To、Call-ID和CSeq,确保路由正确: - **Via**:响应按请求的Via列表逆序路由。服务器复制所有Via字段到响应中,然后发送到顶部Via地址。每个代理检查顶部Via是否匹配自身地址,匹配后删除该Via并转发到下一个[^2][^4]。例如,在引用[4]的响应中: ``` Via: SIP/2.0/UDP cavendish.kings.cambridge.example.com;tag=512A6;branch=z9hG4bK0834;received=192.0.0.2 ``` 这里,received参数表示请求实际来源IP,帮助NAT穿越。 - **From** 和 **To**:直接复制自请求,但响应中To添加标签(tag)以标识对话[^1][^4]。例如: ``` To: <sip:wiliamhopkins@cam.ac.uk>;tag=432 From: J.C. Maxwell <sip:james.maxwell@kings.cambridge.example.com>;tag=34 ``` 这确认了发起方和目标方,标签用于后续事务(如BYE)。 - **Call-ID**:完全复制请求的Call-ID,确保响应关联到同一事务[^1][^4]。例如: ``` Call-ID: 747469e729acd305 ``` 这保持了交互的连续性。 - **CSeq**:复制请求的CSeq(方法名和数字),用于匹配请求和响应。数字不变,表示这是对同一请求的响应[^3][^4]。例如: ``` CSeq: 29 OPTIONS ``` 这对应请求的CSeq,避免因网络延迟导致的混淆。 完整的OPTIONS响应示例(基于引用[4]): ``` SIP/2.0 200 OK Via: SIP/2.0/UDP cavendish.kings.cambridge.example.com;tag=512A6;branch=z9hG4bK0834;received=192.0.0.2 To: <sip:wiliamhopkins@cam.ac.uk>;tag=432 From: J.C. Maxwell <sip:james.maxwell@kings.cambridge.example.com>;tag=34 Call-ID: 747469e729acd305 CSeq: 29 OPTIONS Contact: <sip:william@tutors.cambridge.example.com>;audio;video Allow: INVITE, OPTIONS, ACK, BYE, CANCEL, REFER Content-Type: application/sdp Content-Length: ... ``` 响应体(如SDP)包含服务器支持的能力,如编解码器。 #### 3. **请求-响应交互过程** - **发送请求**:UA发送OPTIONS请求,Via堆栈从顶部开始记录路径(如UA → 代理1 → 代理2 → 服务器)。 - **处理响应**:服务器生成200 OK响应,复制所有Via字段。响应按Via逆序路由:从服务器 → 代理2(检查并删除顶部Via) → 代理1 → UA。分支参数和标签确保事务唯一性[^2][^5]。 - **关键作用**:Via确保响应可靠返回;Call-ID和CSeq防止消息重复或乱序;From和To标识端点。例如,CSeq数字递增可处理重传请求[^3]。 #### 注意事项 - OPTIONS请求不建立会话,仅用于能力查询,常用于网络诊断或服务发现。 - 字段错误(如Via不匹配)会导致响应丢弃,确保使用标准SIP库(如PJSIP)以避免实现问题[^2][^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值