2020牛客暑期多校训练营(第一场)A B-Suffix Array

首先是题解的做法

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#include
#define up(i,a,b)  for(int i=a;i
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
typedef unsigned long long ull;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
ll read()
{
    char ch = getchar(); ll x = 0, f = 1;
    while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}
typedef pair<int, int> pir;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lrt root<<1
#define rrt root<<1|1
const int N = 1e5 + 10;
char ss[2 * N];
int n;
int a[2 * N];
int rk[2 * N], sa[2 * N], t[2 * N], t2[2 * N], c[2 * N], height[2 * N];
int nxt[N * 2][3];
void da(int *s,int sz)
{
    int *x = t, *y = t2;
    int m = sz;
    up(i, 0, m)c[i] = 0;
    up(i, 0, n)c[x[i] = s[i]]++;
    up(i, 1, m)c[i] += c[i - 1];
    dwd(i, n - 1, 0)sa[--c[x[i]]] = i;
    for (int k = 1; k <= n; k <<= 1)
    {
        int p = 0;
        up(i, n - k, n)y[p++] = i;
        up(i, 0, n)if (sa[i] >= k)y[p++] = sa[i] - k;
        up(i, 0, m)c[i] = 0;
        up(i, 0, n)c[x[y[i]]]++;
        up(i, 1, m)c[i] += c[i - 1];
        dwd(i, n - 1, 0)sa[--c[x[y[i]]]] = y[i];
        swap(x, y);
        x[sa[0]] = 0;
        p = 1;
        up(i, 1, n)
            x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++;
        if (p >= n)break;
        m = p;
        //cout << ")" << endl;
    }
}
void test(int *s)
{
    cout << "suffix:" << endl;
    up(i, 0, n)
    {
        //cout << "sa" << sa[i] << endl;
        up(j, sa[i], n)cout << s[j] << " ";
        cout << endl;
        cout <<"h: "<< height[i] << endl;
    }
}
int main()
{
    while (~scanf("%d\n", &n))
    {
        scanf("%s", ss + 1);
        upd(i, 0, n + 1)up(j, 0, 2)nxt[i][j] = 0;
        dwd(i, n, 1)
        {
            up(j, 0, 2)nxt[i][j] = nxt[i + 1][j];
            a[i - 1] = nxt[i][ss[i] - 'a'] ? n - nxt[i][ss[i] - 'a'] + i : 1;
            nxt[i][ss[i] - 'a'] = i;
        }
        a[n] = 0;
        n++;
        da(a, n + 5);
        //test(a);
        upd(i, 1, n-1)
        {
            printf("%d ", sa[i] + 1);
        }
        printf("\n");
    }
    return 0;
}

再说说正常人能想到的做法。
首先观察,其实这个字符串本质上是01串,替换a为0,替换b为1。
可以发现,字符串形如11110000111000111000交替出现。
在发现,当算后缀的B函数的时候,后缀的值要么只会从原来的值突变为0,要么不变。
我们考虑字符串111011101。考虑两个前缀,111011101,11101,发现他的B函数分别为012021242,01202,可以发现,他的B函数的前缀相同,0120,所以这个时候,需要比较111011101和11101中,第一个字符串字符串11101和第二个字符串的后缀1的大小。我们可以大胆假设,该字符串通过B函数排序,仅仅之和当前(例如1)数字,与后面不和他一样的数字的距离有关。
1.当距离不等时
有形如111011101,1101,B函数前两位都是01,第三位开始不同,一个是1,一个是0,发现距离越近,B函数越小。
2.当距离相等时
有111011101,11101,此时后0后面的后缀有关。于是我们先通过后缀数字进行排序,然后通过求sa函数得到。
可以简单证明正确性:
当有连续个1(或者0)时,函数值为01111111…,知道出现和他不同的数字,这个时候函数值是01111…1110,我们可以发现01两个数字都已经出现过了,故后面的数字一定不是0,且,当进行后缀排序的时候,他在当前后缀中的函数值,就是B函数的值。比如原数组1110111,我们观察后缀10111,后3个1求得的函数值,和B函数值相等(因为在他之前01都已经出现过了,不会再出现因为他之前的0或者1被去掉,从而影响当前位置成为0,即改变函数值)。
所以第一步,我们求整个数组的B函数,进行后缀排序。然后我们重新计算一个值,即当前位置的下一个,和自己不一样的位置在哪里。(这里如果使用计算长度的话,会有边界问题)然后我们需要考虑,末尾的情况,因为末尾假设是000的话,没有下一个1。这个时候设置一个虚拟的节点在n+1位置,令他的rk值即排名为-1。保证后面为空的时候,比其他任何后缀要小。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#include
#define up(i,a,b)  for(int i=a;i
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
typedef unsigned long long ull;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
ll read()
{
	char ch = getchar(); ll x = 0, f = 1;
	while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}
typedef pair<int, int> pir;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lrt root<<1
#define rrt root<<1|1
const int N = 1e5 + 10;
char ss[2 * N];
int n;
int a[2 * N];
int rk[2 * N], sa[2 * N], t[2 * N], t2[2 * N], c[2 * N], height[2 * N];
int id[2 * N], dis[2 * N];
void da(int *s, int sz)
{
	int *x = t, *y = t2;
	int m = sz;
	up(i, 0, m)c[i] = 0;
	up(i, 0, n)c[x[i] = s[i]]++;
	up(i, 1, m)c[i] += c[i - 1];
	dwd(i, n - 1, 0)sa[--c[x[i]]] = i;
	for (int k = 1; k <= n; k <<= 1)
	{
		int p = 0;
		up(i, n - k, n)y[p++] = i;
		up(i, 0, n)if (sa[i] >= k)y[p++] = sa[i] - k;
		up(i, 0, m)c[i] = 0;
		up(i, 0, n)c[x[y[i]]]++;
		up(i, 1, m)c[i] += c[i - 1];
		dwd(i, n - 1, 0)sa[--c[x[y[i]]]] = y[i];
		swap(x, y);
		x[sa[0]] = 0;
		p = 1;
		up(i, 1, n)
			x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++;
		if (p >= n)break;
		m = p;
		//cout << ")" << endl;
	}
}
void getheight()
{
	up(i, 0, n)rk[sa[i]] = i;
	int j = 0;
	int k = 0;
	up(i, 0, n)
	{
		if (k)k--;
		j = sa[rk[i] - 1];
		while (a[i + k] == a[j + k])k++;
		height[rk[i]] = k;
	}
}
void test(int *s)
{
	cout << "suffix:" << endl;
	up(i, 0, n)
	{
		cout << "sa" << sa[i] << endl;
		up(j, sa[i], n)cout << s[j] << " ";
		cout << endl;
		cout << "h: " << height[i] << endl;
	}
}
bool cmp(int a, int b)
{
	if (dis[a]-a == dis[b]-b)
	{
		return rk[dis[a]] < rk[dis[b]];
	}
	else return	dis[a] - a < dis[b] - b;
}
int main()
{
	while (~scanf("%d\n", &n))
	{
		scanf("%s", ss + 1);
		int posa = -1, posb = -1;
		upd(i, 1, n)
		{
			if (ss[i] == 'a')
			{
				if (posa == -1)a[i-1] = 1;
				else a[i-1] = i - posa + 1;
				posa = i;
			}
			else {
				if (posb == -1)a[i-1] = 1;
				else a[i-1] = i - posb + 1;
				posb = i;
			}
		}
		a[n] = 0;
		n += 1;
		da(a, n + 5);
		getheight();
		//test(a);
		n -= 1;
		upd(i, 1, n)id[i] = i;
		posa = -1; posb = -1;
		dwd(i, n,1)
		{
			if(ss[i]=='a')
			{
				if (posb == -1)dis[i] = n + 1;
				else dis[i] = posb;
				posa = i;
			}
			else {
				if (posa == -1)dis[i] = n + 1;
				else dis[i] = posa;
				posb = i;
			}
		}
		rk[n + 1] = -1;
		sort(id + 1, id + 1 + n,cmp);
		upd(i, 1, n)
			printf("%d ", id[i]);
		printf("\n");
	}
	return 0;
}

你可能感兴趣的:(后缀数组,思维,多校)