初学后缀数组

后缀数组就是把一个字符串的后缀排序,然后就可以再乱搞一些东东。

DC3看着就累,还是倍增好。

当初看代码觉得虽然不长但是理解起来并不容易,各种数组有各自的意义很容易搞混。

特别是要理解基数排序。每次按第二关键字排序再按第一关键字排序,可以保证在第一关键字相同时第二关键字的大小关系也正确。

白书上的数组有s(字符串)、sa、x、y、c,计算height还用到了rank。

我的代码是照着白书上的写的(发现白书上的代码有几个小的细节问题)。

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn=1e6+10;
char s[maxn];

int n,m,p;
int sa[maxn],x[maxn],y[maxn],c[maxn];
//sa当前排好的后缀 x每个后缀的rank y由第二关键字排好的后缀 c记录每种相同的个数及前缀和 
int height[maxn];

int main() {
	gets(s); n=strlen(s); m='z'+1;
	for(int i=0;i<n;++i) c[x[i]=s[i]]++;
	for(int i=1;i<m;++i) c[i]+=c[i-1];
	for(int i=n-1;i>=0;--i) sa[--c[x[i]]]=i;//sa[c[w]-1]为放入目前rank为w的可放的最后一个位置 
	for(int k=1;k<=n;k<<=1) {
		p=0;
		for(int i=n-1;i>=n-k;--i) y[p++]=i;//把已经没有第二关键字的放在前 
		for(int i=0;i<n;++i) if(sa[i]>=k) y[p++]=sa[i]-k;//根据第二关键字放入对应的第一关键字位置(后缀的起始位置) 
		for(int i=0;i<m;++i) c[i]=0;
		for(int i=0;i<n;++i) c[x[i]]++;
		for(int i=1;i<m;++i) c[i]+=c[i-1];//记录每个后缀第一关键字的信息 
		for(int i=n-1;i>=0;--i) sa[--c[x[y[i]]]]=y[i];//根据第一关键字排序 
		
		swap(x,y);//此时y已经无用,我们需要更新x数组必须用到曾经的x数组(判断相同的情况) 
		p=1; x[sa[0]]=0;
		for(int i=1;i<n;++i) x[sa[i]]= y[sa[i-1]]==y[sa[i]]&&sa[i-1]+k<n&&sa[i]+k<n&&y[sa[i-1]+k]==y[sa[i]+k]? p-1:p++;
		if(p>=n) break;
		else m=p;
	}
	for(int i=0;i<p;++i) printf("%d ",sa[i]+1); printf("\n");
	int k=0;
	for(int i=0;i<n;++i) {
		if(!x[i]) {
			k=0; continue;
		}
		if(k) k--;
		int j=sa[x[i]-1];
		while(s[i+k]==s[j+k]) k++;
		height[x[i]]=k;
	}
	for(int i=1;i<n;++i) printf("%d ",height[i]);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 的输入框可以接收用户输入的数字和运算符,用户输入后,程序会将其压入相应的堆栈中。当用户点击“=”按钮时,程序会从堆栈中依次取出数字和运算符,按照运算符的优先级进行计算,最终得出结果并显示在计算器的最下方输出框中。 设计这款计算器需要考虑以下几个方面: 1. 如何判断用户输入的是数字还是运算符? 可以通过判断输入的字符是否为数字或者运算符来区分。如果是数字,则将其转换为整数并压入数字堆栈中;如果是运算符,则将其压入运算符堆栈中。 2. 如何处理运算符的优先级? 可以将运算符按照优先级从高到低依次压入运算符堆栈中。当需要进行计算时,从运算符堆栈中取出一个运算符,再从数字堆栈中取出两个数字进行计算,将计算结果压入数字堆栈中。如果运算符堆栈中还有运算符,则继续取出运算符进行计算,直到运算符堆栈为空为止。 3. 如何处理错误输入? 需要对用户输入进行合法性检查,如果输入的字符不是数字或者运算符,则提示用户输入有误。如果用户输入的运算符不支持,则提示用户该运算符不支持。 4. 如何处理除数为的情况? 需要在进行除法运算时进行判断,如果除数为,则提示用户除数不能为。 通过以上几个方面的考虑,可以设计出一款简单的利用堆栈执行的计算器,帮助初学数据结构的小伙伴更好地理解堆栈的应用。 ### 回答2: 首先,我们需要了解堆栈(stack)的概念。堆栈是一种特殊的线性数据结构,只允许在一端进行插入和删除操作,这一端被称为栈顶。该数据结构的特点是先进后出,后进先出的顺序。 接下来,我们可以开始设计计算器。首先,需要设计一个函数来将表达式转换成逆波兰表达式,也就是将中缀表达式转换成后缀表达式。这个过程可以使用堆栈来完成。具体方法如下: 1. 从左往右遍历中缀表达式的每一个元素,若该元素为数字则直接输出到结果中,若为运算符则执行以下步骤: 2. 若该运算符为“(”,则将其压入s2堆栈中; 3. 若该运算符为“)”,则将s2中的运算符弹出并压入s1中,直到遇到左括号,将左括号弹出丢弃; 4. 若该运算符的优先级比栈顶运算符的优先级高,则将其压入s2堆栈中; 5. 若该运算符的优先级比栈顶运算符的优先级低或相等,则将s2中的运算符弹出并压入s1中,重复步骤4,直到该运算符可以压入s2堆栈为止; 6. 当表达式遍历完后,若s2中仍然有运算符,则将它们依次弹出并压入s1中。 接下来,我们可以设计一个函数来计算后缀表达式的值。具体方法如下: 1. 从左往右遍历后缀表达式的每一个元素,若该元素为数字则压入s1堆栈中,若为运算符则执行以下步骤: 2. 从s1堆栈中弹出两个数字,分别为a和b; 3. 根据该运算符计算a和b的值,将结果压入s1堆栈中; 4. 重复步骤1-3,直到后缀表达式遍历完毕; 5. 最终s1中只剩下一个数字,即为计算结果。 最后,我们可以设计一个函数来实现整个计算器的功能。具体方法如下: 1. 从键盘读入一个中缀表达式; 2. 调用第一个函数将中缀表达式转换成后缀表达式; 3. 调用第二个函数计算后缀表达式的值,并输出结果。 通过以上操作,我们可以设计一个简单的利用堆栈执行的计算器,对于初学数据结构的小伙伴来说,可以作为一个良好的练手项目来帮助理解堆栈的概念。 ### 回答3: 为了创建一款简单易用的堆栈计算器,我们需要考虑以下几个方面: 1. 堆栈结构的实现 我们需要使用堆栈结构存储数字和运算符。堆栈是一种后进先出(LIFO)的数据结构,我们可以使用一个数组来模拟它们的实现。堆栈的基本操作是 push(压栈)和 pop(弹栈)。当我们 push 一个元素时,它会被添加到堆栈的顶部;当我们 pop 一个元素时,它会被从堆栈的顶部弹出。 下面是一个简单的实现示例: class Stack: def __init__(self): self.stack = [] def push(self, item): self.stack.append(item) def pop(self): if not self.is_empty(): return self.stack.pop() def is_empty(self): return len(self.stack) == 0 2. 计算器的设计 在计算器中,我们需要处理数字和运算符。当我们遇到一个数字时,我们将其 push 到数字堆栈中;当遇到一个运算符时,我们将其 push 到运算符堆栈中。我们还需要处理一些特殊字符,例如括号、小数点和正负号。 在计算器中,我们使用两个堆栈来处理数字和运算符,而且我们需要注意运算符之间的优先级。当一个运算符的优先级比另一个运算符高时,它会先被处理。为了实现这个功能,我们可以定义一个操作符优先级的字典。 下面是一个简单的堆栈计算器的示例代码: class Calculator: priority = {'+': 1, '-': 1, '*': 2, '/': 2} def __init__(self): self.numbers = Stack() self.operators = Stack() def calculate(self, expr): for c in expr: if c.isdigit(): self.numbers.push(float(c)) elif c == '.': num = self.numbers.pop() self.numbers.push(float(str(num) + c)) elif c in self.priority: while not self.operators.is_empty() and self.priority[c] <= self.priority[self.operators.stack[-1]]: self.do_math() self.operators.push(c) elif c == '(': self.operators.push(c) elif c == ')': while self.operators.stack[-1] != '(': self.do_math() self.operators.pop() elif c == '-': if not self.numbers.is_empty() and isinstance(self.numbers.stack[-1], float) and not isinstance(self.operators.stack[-1], str): num = self.numbers.pop() self.operators.push('*') self.numbers.push(-num) else: self.numbers.push(c) else: continue while not self.operators.is_empty(): self.do_math() return self.numbers.pop() def do_math(self): op = self.operators.pop() num2 = self.numbers.pop() num1 = self.numbers.pop() if op == '+': self.numbers.push(num1 + num2) elif op == '-': self.numbers.push(num1 - num2) elif op == '*': self.numbers.push(num1 * num2) else: self.numbers.push(num1 / num2) 3. 总结 在本示例中,我们介绍了一个简单的堆栈计算器的实现。堆栈是一种强大的数据结构,它在计算器中发挥着重要的作用。我们通过实现一个简单的堆栈计算器来展示堆栈的使用方法。希望这篇文章能够对初学数据结构和算法的小伙伴有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值