hashit

题目大意

你有一个字符串S,开始为空,现在有两种操作:
1. 在S后面加入一个字符c
2. 删除S最后一个字符(保证进行该操作时S不为空)
每次操作后输出当前S中有多少个不同的连续子串。

操作数不大于100000

在线做法

这道题可以离线建trie,然后打个sam。(然而我打的是在线)

在线维护字符串,维护插入、删除操作,很容易想到后缀平衡树。

如果字符串是静态的,统计S中不同子串个数的经典做法是用后缀数组,构造出height数组后统计答案。

由于后缀平衡树维护后缀的顺序,所以现在我们可以动态算height数组。采用hash,可以二分height[i]的大小,然后hash判断两个后缀相同长度的连续一段是否相同即可。
时间复杂度O(nlogn)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int maxn=100005,mo=5371297,pri=832189,M[2]={67,71};

typedef long long LL;

typedef unsigned long long ULL;

const LL Inf=(LL)1<<60;

int n,root,fa[maxn],son[maxn][2],fix[maxn],len,pre[maxn],nxt[maxn],height[maxn];

LL l[maxn],r[maxn],rank[maxn],ans;

ULL Hash[2][maxn],Power[2][maxn];

char s[maxn],c[maxn];

void rebuild(int x,LL L,LL R)
{
    if (!x) return;
    l[x]=L; r[x]=R; rank[x]=l[x]+r[x];
    rebuild(son[x][0],l[x],rank[x]>>1);
    rebuild(son[x][1],rank[x]>>1,r[x]);
}

void Rotate(int x,int t,LL l,LL r)
{
    int y=fa[x];
    if (y==root) root=x;else
    {
        if (son[fa[y]][0]==y) son[fa[y]][0]=x;else son[fa[y]][1]=x;
    }
    fa[x]=fa[y];
    son[y][t]=son[x][t^1]; fa[son[y][t]]=y; son[x][t^1]=y; fa[y]=x;
    rebuild(x,l,r);
}

bool cmp(int x,int y)
{
    return s[x]<s[y] || s[x]==s[y] && rank[x-1]<rank[y-1];
}

int ran(int x)
{
    return (LL)(s[x]+x)*pri%mo;
}

bool Same(int x,int y,int l)
{
    if (x<l) return 0;
    for (int j=0;j<2;j++)
        if (Hash[j][x]-Hash[j][x-l]*Power[j][l]!=Hash[j][y]-Hash[j][y-l]*Power[j][l]) return 0;
    return 1;
}

int lcp(int x,int y)
{
    if (x>y) x^=y^=x^=y;
    int l,r,mid;
    for (l=1,r=x+1,mid=l+r>>1;l<r;mid=l+r>>1)
        if (Same(x,y,mid)) l=mid+1;else r=mid;
    return l-1;
}

void update(int x)
{
    ans-=nxt[x]-height[nxt[x]];
    height[x]=lcp(x,pre[x]);
    height[nxt[x]]=lcp(nxt[x],x);
    ans+=x-height[x]+nxt[x]-height[nxt[x]];
}

void insert(int x,int i,LL l,LL r)
{
    LL mid=l+r>>1;
    if (cmp(x,i))
    {
        if (son[i][0]) insert(x,son[i][0],l,mid);
        else
        {
            son[i][0]=x; fa[x]=i; rebuild(x,l,mid);
            pre[x]=pre[i]; nxt[pre[x]]=x;
            nxt[x]=i; pre[i]=x;
        }
        if (fix[i]>fix[son[i][0]]) Rotate(son[i][0],0,l,r);
    }else
    {
        if (son[i][1]) insert(x,son[i][1],mid,r);
        else
        {
            son[i][1]=x; fa[x]=i; rebuild(x,mid,r);
            nxt[x]=nxt[i]; pre[nxt[x]]=x;
            pre[x]=i; nxt[i]=x;
        }
        if (fix[i]>fix[son[i][1]]) Rotate(son[i][1],1,l,r);
    }
}

void Delete(int x)
{
    while (son[x][0]>0 || son[x][1]>0)
        if (!son[x][0] || son[x][1]>0 && fix[son[x][0]]>fix[son[x][1]]) Rotate(son[x][1],1,l[x],r[x]);
        else Rotate(son[x][0],0,l[x],r[x]);
    if (root==x) root=0;else
    {
        ans-=x-height[x]+nxt[x]-height[nxt[x]];
        if (x==son[fa[x]][0]) son[fa[x]][0]=0;else son[fa[x]][1]=0;
        pre[nxt[x]]=pre[x]; nxt[pre[x]]=nxt[x];
        height[nxt[x]]=min(height[nxt[x]],height[x]);
        //height[nxt[x]]=lcp(nxt[x],pre[x]);
        ans+=nxt[x]-height[nxt[x]];
    }
}

int main()
{
    scanf("%s",c+1);
    n=strlen(c+1);
    Power[0][0]=Power[1][0]=1;
    for (int i=1;i<=n;i++)
        for (int j=0;j<2;j++) Power[j][i]=Power[j][i-1]*M[j];
    for (int i=1;i<=n;i++)
    {
        if (c[i]=='-') Delete(len--);else
        {
            s[++len]=c[i];
            fix[len]=ran(len);
            fa[len]=son[len][0]=son[len][1]=0;
            for (int j=0;j<2;j++) Hash[j][len]=Hash[j][len-1]*M[j]+s[len]-'a';
            if (len==1)
            {
                root=1;
                l[1]=0; rank[1]=r[1]=Inf;
                ans=1;
                printf("1\n");
                continue;
            }
            insert(len,root,0,Inf);
            update(len);
        }
        printf("%lld\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Three.js是一个流行的JavaScript 3D图形库,它提供了许多功能,其中包括碰撞测试。在Three.js中进行碰撞测试需要使用到物理引擎库。以下是使用Three.js进行碰撞测试的基本步骤: 1. 导入Three.js和物理引擎库,例如 Ammo.js 或 Cannon.js。 2. 创建Three.js场景,并添加相机和光源等元素。 3. 创建物理引擎世界,并将Three.js场景中的物体添加到物理引擎世界中。 4. 创建碰撞体,例如球体、盒子或平面等,用于检测物体之间的碰撞。 5. 在每一帧中更新物理引擎世界和Three.js场景。 6. 使用物理引擎库提供的碰撞测试函数,例如 .raycast() 或 .intersectObjects(),检测碰撞体是否与场景中的物体相交。 7. 根据碰撞测试的结果,执行相应的操作,例如改变物体的颜色或位置等。 下面是一段使用Three.js和Cannon.js进行球体碰撞测试的示例代码: ```javascript // 创建Three.js场景 const scene = new THREE.Scene(); // 创建相机和光源等元素 // 创建物理引擎世界 const world = new CANNON.World(); world.gravity.set(0, -9.82, 0); // 将Three.js场景中的物体添加到物理引擎世界中 // ... // 创建碰撞体 const ballShape = new CANNON.Sphere(1); const ballBody = new CANNON.Body({ mass: 1 }); ballBody.addShape(ballShape); ballBody.position.set(0, 10, 0); world.addBody(ballBody); // 在每一帧中更新物理引擎世界和Three.js场景 function animate() { requestAnimationFrame(animate); // 更新物理引擎世界 world.step(1 / 60); // 更新Three.js场景中的物体 // ... // 使用 .raycast() 函数检测碰撞体是否与场景中的物体相交 const raycastResult = new CANNON.RaycastResult(); const from = ballBody.position.clone(); const to = new THREE.Vector3(0, 0, 0); const raycastOptions = { skipBackfaces: true, collisionFilterMask: 0xffffffff, }; world.raycastClosest(from, to, raycastOptions, raycastResult); if (raycastResult.hasHit) { // 根据碰撞测试的结果执行相应的操作 // ... } // 渲染Three.js场景 renderer.render(scene, camera); } animate(); ``` 以上代码只是一个简单的示例,您需要根据具体的场景和需求进行相应的修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值