void Getnext(int next[],String t)
{
int j=0,k=-1;
next[0]=-1;
while(j<t.length-1)
{
if(k == -1 || t[j] == t[k])
{
j++;k++;
next[j] = k;
}
else k = next[k];//此语句是这段代码最反人类的地方,如果你一下子就能看懂,那么请允许我称呼你一声大神!
}
}
int KMP(String s,String t)
{
int next[MaxSize],i=0;j=0;
Getnext(t,next);
while(i<s.length&&j<t.length)
{
if(j==-1 || s[i]==t[j])
{
i++;
j++;
}
else j=next[j]; //j回退。。。
}
if(j>=t.length)
return (i-t.length); //匹配成功,返回子串的位置
else
return (-1); //没找到
}
这篇博客写的比较好
模板题传送门
#include
#include
using namespace std;
const int N = 1e5 + 10;
char p[N];
int ne[N];
int len;
void getnext()
{
ne[1] = 0;
for(int i = 2, j = 0; i <= len; ++ i)
{
while(j && p[i] != p[j + 1]) j = ne[j];
if(p[i] == p[j + 1]) j ++ ;
ne[i] = j;
}
}
int main()
{
std::ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T;
cin >> T;
while(T --)
{
cin >> p + 1;
len = strlen(p + 1);
getnext();
int L = len - ne[len];
if(!ne[len]) cout << len << endl;
else if(len % L == 0) cout << "0" << endl;
else cout << L - len % L << endl;
}
cout.flush();
return 0;
}
模板题传送门
/* POJ2752 Seek the Name, Seek the Fame */
#include
#include
char s[400000+1];
int next[400000+1];
int result[400000+1];
void setnext(char s[], int next[], int len)
{
next[0] = -1;
int i = 0, j = -1;
while (i < len)
{
if (j == -1 || s[i] == s[j]) {
++i;
++j;
next[i] = j;
} else
j = next[j];
}
}
int main(void)
{
int count, t, i;
while(scanf("%s", s) != EOF) {
int len = strlen(s);
// 计算next[]数组值
setnext(s, next, len);
// 计算结果:从字符串的尾部开始,下一个只能在与后缀相同的前缀字符串中
count = 0;
t = next[len - 1];
while(t != -1) {
if(s[t] == s[len - 1])
result[count++] = t + 1;
t = next[t];
}
// 输出结果
for(i=count-1; i>=0; i--)
printf("%d ", result[i]);
printf("%d\n", len);
}
return 0;
}
HDU4763
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N=1000005;
char s[N];
int Next[N],l;
void get_Next()
{
int i=0,j=-1;
Next[0]=-1;
while(i<l)
{
if(j==-1||s[i]==s[j])
Next[++i]=++j;
else
j=Next[j];
}
}
int KMP()
{
int i,j;
for(i=Next[l]; i; i=Next[i])
for(j=2*i; j<=l-i; j++)
if(Next[j]==i)
return i;
return 0;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",s);
int ans=0,k,i,j;
l=strlen(s);
get_Next();
// for(i=1; i<=l; i++)
// printf("%d ",Next[i]);
// printf("\n");
printf("%d\n",KMP());
}
return 0;
}
--------------------------------------------------分割线-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#include
#include
#include
#include
#include
#include
#include
#define INF 0x3f3f3f3f
#define f first
#define s second
#define IOS std:ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
using namespace std;
const int maxn = 2e5 + 10;
typedef long long LL;
typedef pair <int,int> PII;
int a,b,n,t;
char s[maxn];
char Ma[maxn];
int Mp[maxn];
int main()
{
IOS;
while(cin >> s)
{
int l = 0;
int len = strlen(s);
Ma[l ++] = '$';
Ma[l ++] = '#';
for(int i = 0; i < len; ++ i)
{
Ma[l ++] = s[i];
Ma[l ++] = '#';
}
Ma[l] = 0;
int mx = 0, id = 0;//mx是右边界,目前最大回文半径的中心
for(int i = 0; i < l; ++ i)
{
Mp[i] = mx > i ? min(Mp[2 * id - i],mx - i) : 1;
while(Ma[i + Mp[i]] == Ma[i - Mp[i]]) Mp[i] ++;
if(i + Mp[i] > mx)
{
mx = i + Mp[i];
id = i;
}
}
int ans = 0;
for(int i = 0; i < l; ++ i)
ans = max(ans,Mp[i]);
cout << ans - 1<< endl;
}
return 0;
}
模板传送hdu3613
这是核心代码
for(int i = 0; i < len - 1; ++ i)
{
int tmp = 0;
int num = Mp[i + 2] - 1;
if(num == i + 1)tmp += hash[i];
num = Mp[i + len + 2] - 1;
if(num == len - i - 1) tmp += hash[len - 1] - hash[i];
if(tmp > ans) ans = tmp;
}
完整代码如下
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MEM(a,al) memset(a,al,sizeof(a))
#define sfx(x) scanf("%lf",&x)
#define sfxy(x,y) scanf("%lf%lf",&x,&y)
#define sdx(x) scanf("%d",&x)
#define sdxy(x,y) scanf("%d%d",&x,&y)
#define pfx(x) printf("%.0f\n",x)
#define pfxy(x,y) printf("%.6f %.6f\n",x,y)
#define pdx(x) printf("%d\n",x)
#define pdxy(x,y) printf("%d %d\n",x,y)
#define getArray(a,len) for(int ia = 0; ia < len; ia++) scanf("%d",&a[ia])
#define printArray(a,len) for(int ia = 0; ia < len; ia++) printf("%d%c",a[ia],(ia==len-1)?'\n':' ')
#define fora(i,n) for(i = 0; i < n; i++)
#define fora1(i,n) for(i = 1; i <= n; i++)
#define foraf(i,n) for(int i = 0; i < n; i++)
#define foraf1(i,n) for(int i = 1; i <= n; i++)
#define ford(i,n) for(i = n-1; i >= 0; i--)
#define ford1(i,n) for(i = n; i > 0; i--)
#define fordf(i,n) for(int i = n-1; i >= 0; i--)
#define fordf1(i,n) for(int i = n; i > 0; i--)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define hash Hash
#define f first
#define s second
using namespace std;
const int N = 5e5 + 10;
typedef pair<int,int> PII;
typedef long long LL;
char a[N], Ma[N * 2];
int T, hash[N], ne[26], Mp[N * 2];
inline void Manarcher(char a[],int len)
{
int l = 0;
Ma[l ++] = '$';
Ma[l ++] = '#';
for(int i = 0; i < len; ++ i)
{
Ma[l ++] = a[i];
Ma[l ++] = '#';
}
Ma[l] = 0;
int mx = 0, idx = 0;
for(int i = 0; i < l; ++ i)
{
Mp[i] = mx > i ? min(Mp[2 * idx - i],mx - i) : 1;
while(Ma[i + Mp[i]] == Ma[i - Mp[i]]) Mp[i] ++;
if(i + Mp[i] > mx)
{
mx = i + Mp[i];
idx = i;
}
}
}
int main()
{
IOS;
cin >> T;
while(T --)
{
for(int i = 0; i < 26; ++ i)
cin >> ne[i];
cin >> a;
int len = strlen(a);
hash[0] = ne[a[0] - 'a'];
for(int i = 1; i < len; ++ i)
hash[i] = ne[a[i] - 'a'] + hash[i - 1];
Manarcher(a,len);
int ans = 0;
for(int i = 0; i < len - 1; ++ i)
{
int tmp = 0;
int num = Mp[i + 2] - 1;
if(num == i + 1)tmp += hash[i];
num = Mp[i + len + 2] - 1;
if(num == len - i - 1) tmp += hash[len - 1] - hash[i];
if(tmp > ans) ans = tmp;
}
cout << ans << endl;
}
return 0;
}
int j = i + (i & 1);
if(j / 2 - 1 >= 0) Prefix[j / 2 - 1] ++;
if((i + Mp[i] - 1 - (i & 1)) / 2 >= 0) Prefix[(i + Mp[i] - 1) / 2] --;
for(int i = 1; i < len; ++ i)
Prefix[i] += Prefix[i - 1];
Trie,又叫字典树,字典你现在可以想一想你手上的牛津第八版词典,假设你是这本词典的主编,我们要去查找一个单词这么查,怎么添加一个热点词汇?
我们一般查找单词都是按26个字母排序去检索的,而添加也是一样的,那为什么我们可以怎么轻松去完成这些步骤?那是因为词典特有的存储方式。我们来看一下实体图:
这个就是词典和Tire的存储方式。我们还是给他一个官方的定义:
Trie树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高
其实它可以看成一个二维数组,保存一些字符串->值的对应关系。基本上,它跟Map的 HashMap 功能相同,都是 key-value 映射,是一个键值对!只不过 Trie 的 key 只能是字符串。
至于Trie树的实现,可以用数组,也可以用指针动态分配,我做题时为了方便就用了数组,静态分配空间。当然我们后面会提到动态如何去实现。
Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。当然后面会详细解释它。
Trie特点及方法
Trie树的基本性质可以归纳为:
(1)根节点不包含字母,除根节点意外每个节点只包含一个字母。
(2)从根节点到某一个节点,路径上经过的字符连接起来,为该节点存储的单词。
(3)每个节点的所有子节点包含的单词不相同。
Trie树有一些特性:
其实就是基本性质扩展到实际问题!
1)根节点不包含字母,除根节点外每一个节点都只包含一个字母。
2)从根节点到某一节点,路径上经过的字母连接起来,为该节点对应的单词。
3)每个节点的所有子节点包含的字母都不相同。
4)如果字母的种数为n,则每个结点的出度为n,这也是空间换时间的体现,浪费了很多的空间。
5)插入查找的复杂度为O(n),n为单词长度。
插入,查找基本思想(以查单词为例):
1、插入过程
对于一个单词,从根开始,沿着单词的各个字母所对应的树中的节点分支向下走,直到单词遍历完,将最后的节点标记为红色,表示该单词已插入Trie树。
2、查询过程
同样的,从根开始按照单词的字母顺序向下遍历trie树,一旦发现某个节点标记不存在或者单词遍历完成而最后的节点未标记为红色,则表示该单词不存在,若最后的节点标记为红色,表示该单词存在。
模板
好了有了一些基本点知识点,我们来用他们去解决实际问题:
我们就想怎么去向词典中加东西,和怎么去查询一个单词是否存在?
插入
代码:
int son[N][26], cnt[N], idx;
// 0号点既是根节点,又是空节点
// son[][]存储树中每个节点的子节点
// cnt[]存储以每个节点结尾的单词数量
我们还是以画图的方式去理解:
// 插入一个字符串
void insert(char *str)
{
int p = 0;
for (int i = 0; str[i]; i ++ )
{
int u = str[i] - 'a';
if (!son[p][u]) son[p][u] = ++ idx;
p = son[p][u];
}
cnt[p] ++ ;
}
查询
当然查询与插入思想一致,我们在这里不做过多的强调与解释!!
// 查询字符串出现的次数
int query(char *str)
{
int p = 0;
for (int i = 0; str[i]; i ++ )
{
int u = str[i] - 'a';
if (!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
1、插入
假设存在字符串str,Trie树的根结点为root。i=0,p=root。
1)取str[i],判断p->next[str[i]-‘a’]是否为空,若为空,则建立结点temp,并将p->next[str[i]-‘a’]指向temp,然后p指向temp;若不为空,则p=p->next[str[i]-‘a’];
2)i++,继续取str[i],循环1)中的操作,直到遇到结束符’\0’,此时将当前结点p中的 exist置为true。
2、查找
假设要查找的字符串为str,Trie树的根结点为root,i=0,p=root
1)取str[i],判断判断p->next[str[i]-‘a’]是否为空,若为空,则返回false;若不为空,则p=p->next[str[i]-‘a’],继续取字符。
2)重复1)中的操作直到遇到结束符’\0’,若当前结点p不为空并且st为true,则返回true,否则返回false。
3、删除
删除可以以添加的思想进行删除,它是是一个递归思想(先去查找单词,当他的父节点只有他一个儿子节点那么p->next[i] = NULL;);
(与下面ac代码相比这个要复杂一点,考虑的情况更加多, 为了你理解我建议你直接去看AC的动态代码)
typedef 256 TREE_WIDTH
typedef 128 WORDLENMAX
struct trie_node_st {
int idx;
int pass;
struct trie_node_st *next[TREE_WIDTH];
};
struct trie_node_st root={0, 0, {NULL}};
//清空
void myfree(struct trie_node_st * rt)
{
for(int i=0; i<TREE_WIDTH; i++){
if(rt->next[i]!=NULL){
myfree(rt->next[i]);
rt->next[i] = NULL;
}
}
free(rt);
return;
}
void insert (char *word)
{
int i;
struct trie_node_st *curr, *newnode;
if (word[0]=='\0') return;
curr = &root;
for (i=0; ; ++i) {
if (word[i] == '\0') {
break;
}
curr->pass++;
if (curr->next[word[i]] == NULL)//不存在
{
newnode = (struct trie_node_st*)malloc(sizeof(struct trie_node_st));//开辟空间
memset (newnode, 0, sizeof (struct trie_node_st));//清空
curr->next[word[i]] = newnode;//插入
}
curr = curr->next[word[i]];//存在
}
curr->idx ++;
return 0;
}
int query (struct trie_node_st *rootp)
{
char worddump[WORDLENMAX+1];
int pos=0;
int i;
if (rootp == NULL) return 0;
if (rootp->idx) {
worddump[pos]='\0';
return rootp->idx+rootp->pass;//返回
}
for (i=0;i<TREE_WIDTH;++i) {
worddump[pos++]=i;
query(rootp->next[i]);
pos--;
}
return 0;
}
下面就是个人比较喜欢的静态代码:
#include
using namespace std;
const int N=200010;
int son[N][26],cnt[N],idx;
char str[N];
bool st[N];
int n;
void insert(char str[])
{
int p=0;
for(int i=0;str[i];i++){
int u=str[i]-'a';
if(!son[p][u])son[p][u]=++idx;
p=son[p][u];
}
cnt[p]++;
}
int query(char str[]){
int p=0;
for(int i=0;str[i];i++){
int u=str[i]-'a';
if(!son[p][u])return 0;
p=son[p][u];
}
return cnt[p];
}
int main(){
cin>>n;
while(n--){
string op;
cin>>op>>str;
if(op=="I")insert(str);
else
cout<<query(str)<<endl;
}
return 0;
}