1. 程式人生 > 其它 >【題解】 [HNOI2004] L 語言

【題解】 [HNOI2004] L 語言

題目傳送門

題意

思路

由於你谷的資料有加強,所以主要以你谷上的得分為對照。

55pts on Luogu-Code1 ``` #include using namespace std; #define ll long long const int N=2e6+5; int n,m,lm; string w; int trie[500][27],cnt; bool ex[500]; ll f[N],und[N],tmp;

inline ll lkup(ll l,ll r){
if(l>r) return 0;
int p=0;
ll ret=0;
for(int i=l;i<=r;i++){
int c=w[i]-'a';
if(!trie[p][c]) return ret;
p=trie[p][c];
if(ex[p]) ret=i-l+1;
}
if(ex[p]) ret=r-l+1;
return ret;
}

inline ll find(string s){
memset(f,0,sizeof(f));
tmp=0;
int l=s.length();
for(int i=0;i<l;i++){
f[i]=lkup(0,i);
for(int j=tmp-1;j>=0;j--){
f[i]=max(f[i],und[j]+1+lkup(und[j]+1,(ll)i));//[j+1,i]
if(f[i]i+1) break;
}
f[i]=max(f[i],f[i-1]);
if(f[i]
i+1) und[tmp++]=i;
}
return f[l-1];
}

inline void ins(string str){
int l=str.length();
lm=max(lm,l);
int p=0;
for(int i=0;i<l;i++){
int c=str[i]-'a';
if(!trie[p][c]) trie[p][c]=++cnt;
p=trie[p][c];
}
ex[p]=1;
}

inline string read(){
string s;
char ch=getchar();
while(ch<'a' || ch>'z') ch=getchar();
while(ch>='a' && ch<='z'){
s+=ch;
ch=getchar();
}
return s;
}

int main(){
scanf("%d%d",&n,&m);
while(n--){
w=read();
ins(w);
}
while(m--){
w=read();
printf("%lld\n",find(w));
}
return 0;
}

</details>

#### 貳
Many days later……  
據說正解是AC自動機,那就先打一個AC自動機板子吧(錯誤思想)。  
怎麼保證連著匹配字首呢?打到最後靈光一閃,直接在trie上比較就可以了啊!哪裡用得著什麼AC自動機!

這次打出來和`Code1`有幾點不同:

<details>
<summary 90 pts on Luogu-Code2 </summary>

include<bits/stdc++.h>

using namespace std;

define ll long long

const int S=2e6+5;
const int N=400;
int n,m,ml;
int tr[N100][30],cnt;//,fail[N100](留著做紀念)
char s[N],t[S];
bool ex[N*100],f[S];

inline int query(char r[],int len,int st){
int p=0,ret=0;
len=min(st+ml,len);
for(int i=st;i<len;i++){
int c=r[i]-'a';
if(tr[p][c]){
if(ex[tr[p][c]]){
f[i]=true;
ret=i+1;
}
p=tr[p][c];
} else break;
}
return ret;//maximum prefix length that can reach
}

inline int find(char r[]){
int l=strlen(r);
int ret=0;
ret=query(r,l,0);
for(int i=0;i<l;i++){
if(f[i]){
ret=max(ret,query(r,l,i+1));
}
}
return ret;
}

inline void ins(char r[]){
int l=strlen(r);
ml=max(ml,l);
int p=0;
for(int i=0;i<l;i++){
int c=r[i]-'a';
if(!tr[p][c]) tr[p][c]=++cnt;
p=tr[p][c];
}
ex[p]=1;
}

int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf(" %s",s);
ins(s);
}
while(m--){
memset(f,0,sizeof(f));
scanf(" %s",t);
printf("%d\n",find(t));
}
return 0;
}
/*
遞推,如果不想dfs的話
把多模式串匹配問題化歸成一個模式串匹配問題。
不用fail,不像自動機。
*/

</details>

#### 叄

<details>
<summary> 90 pts on Luogu-Code </summary>

include<bits/stdc++.h>

using namespace std;

define ll long long

const int S=2e6+5;
const int N=400;
int n,m,ml;
int tr[N100][30],cnt;
char s[N],t[S];
bool ex[N
100],f[S];
ll zt;

inline int query(char r[],int len,int st) {
int p=0,ret=0;
len=min(st+ml,len);
for(int i=st; i<len; i++) {
int c=r[i]-'a';
if(tr[p][c]) {
if(ex[tr[p][c]]) {
zt|=(1<<(i-st+1));//f[i]=true;
ret=i+1;
}
p=tr[p][c];
} else break;
}
return ret;
}

inline int find(char r[]) {
int l=strlen(r);
int ret=0;
ret=query(r,l,0);
int i=0;
while(!(zt&1) && zt) zt>>=1,i++;
while(i<l && zt) {
ret=max(ret,query(r,l,i));
zt>>=1; i++;
while(!(zt&1) && zt) zt>>=1,i++;
}
return ret;
}

inline void ins(char r[]) {
int l=strlen(r);
ml=max(ml,l);
int p=0;
for(int i=0; i<l; i++) {
int c=r[i]-'a';
if(!tr[p][c]) tr[p][c]=++cnt;
p=tr[p][c];
}
ex[p]=1;
}

int main() {
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) {
scanf(" %s",s);
ins(s);
}
while(m--) {
memset(f,0,sizeof(f));
scanf(" %s",t);
printf("%d\n",find(t));
}
return 0;
}
/*
遞推,如果不想dfs的話
把多模式串匹配問題化歸成一個模式串匹配問題。
不用fail,不像自動機。
*/

</details>

“人類的使命,在於自強不息地追求完美”,文學巨匠托爾斯泰曾言。雖然至此,已經可以ACLOJ上的題目,但是沒有過你谷上的加強樣例,筆者怎麼會止歇?

#### 肆
One day later……

筆者再次遇到了瓶頸。

“不破不立”,只有敢於挑戰思維定式,才能創造新的成果,突破瓶頸。

### 程式碼實現
***AC Code***

include<bits/stdc++.h>

using namespace std;

define ll long long

const int S=2e6+5;
const int N=405;
int n,m;
int tr[N100][30],cnt,ex[N100],fail[N100];
int f[S],len[N
100],trans[N*100];
char s[N],t[S];

inline int query(char r[]) {
int p=0,x=0;
int l=strlen(r+1);
f[0]=1;
for(int i=1; i<=l; i++) {
p=tr[p][r[i]-'a'];
x=((x<<1)|f[i-1])&((1<<20)-1);
f[i]=(x&len[p])!=0;
}
for(int i=l;i>=1;i--){
if(f[i]) return i;
}
return 0;
}

inline void build_AC(){
queue q;
for(int i=0;i<26;i++){
if(tr[0][i]) q.push(tr[0][i]);
}
while(!q.empty()){
int p=q.front();
q.pop();
for(int i=0;i<26;i++){
if(tr[p][i]){
q.push(tr[p][i]);
fail[tr[p][i]]=tr[fail[p]][i];
}
else tr[p][i]=tr[fail[p]][i];
}
}
for(int i=1;i<=cnt;i++){
int j=i;
while(j){
if(ex[j]) len[i]|=(1<<(ex[j]-1));
j=fail[j];
}
}
}

inline void ins(char r[]) {
int l=strlen(r);
int p=0;
for(int i=0; i<l; i++) {
int c=r[i]-'a';
if(!tr[p][c]) tr[p][c]=++cnt;
p=tr[p][c];
}
ex[p]=l;
}

int main() {
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) {
scanf(" %s",s);
ins(s);
}
build_AC();
while(m--) {
scanf(" %s",t+1);
printf("%d\n",query(t));
}
return 0;
}