Problem 5. Decimal Array Expansion 2018 Goldman Sachs Women's CodeSprint
題意:輸入一個由0-9組成的字串A,給定一組轉換規則S,S[i]把數字i map到一個新的string,如此可將A一層一層擴充套件,直到長度>=M為止,給定一組query,求擴充套件後的字串的區間和。
這一題其實也沒那麼難,但是標了個hard,讓人不戰而敗。。感覺自己還是不夠confident + aggressive ε=(´ο`*)))唉
擴充套件的過程類似於生成一棵樹,第一層節點是A,然後每個節點逐層擴充套件。一個observation是因為相同數字擴充套件規則相同,所以如果A[i]=A[j],那麼擴充套件後A[i]和A[j]對應的子樹是一樣的。同理,同一層相同的節點擴充套件後的子樹也是一樣得。
求區間和等價於求字首和,求[0,...,l]的字首和,需要每個子樹所擁有的葉子個數,以及對應的葉子value之和。
因為每一層不同的節點最多隻有10個,所以用fnum[10][maxk]記錄葉子個數,fnum[i][k]表示第k層數字i對應的子樹葉子節點個數。另外,用fsum[10][maxk]記錄葉子value之和,fsum[i][k]表示第k層數字i對應的子樹葉子節點value之和。
fnum[i][k]和fsum[i][k]可以用樹dp(記憶化搜尋)求得,樹是遞迴結構,感覺很多問題都是遞迴求解。fnum[i][k]=sum_j fnum[j][k+1],fsum[i][k]=sum_j fsum[j][k+1], j is the number expanted from i。遞迴出口是在最大層(葉子層)時,fnum[i][k]=1,fsum[i][k]=i。
求字首和類似於線段樹從頂端往下搜尋,先對於fnum[A[i]][0]進行二分查詢,找出第0層l應該在那一個子樹中,如果在第i個子樹中,返回presum of fsum[0,...,i-1]+dfs(l-presum of fnum[0,...,i-1])。對於子樹中的每個node,也是找到第一個child使得其fnum[i][k]字首和剛好>l,再從該child對應的子樹中搜索,傳入的引數l應該減去child之前的子樹葉子節點數之和。遞迴出口是最大層是返回1,此時找到了對應的l,也恰巧l=1。
開始WA是因為理解錯了題意,以為S[i]長度都一樣。。結束後可以看test case。。然後發現咦居然還有這種input。。
給定M,求最大層數maxlvel也可以由逐層擴充套件模擬實現。第k層記錄0-9出現的頻次,然後k+1層可以根據拓展規則S求出第k層的數字i會生成0-9的數字頻次是多少,之後相加即可。
#include<iostream>
#include<stdio.h>
#include<cstdio>
#include<string>
#include<cmath>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
#include<list>
using namespace std;
const int maxn=20;
int T;
int N;
int M;
int Q;
vector<long>presum;
vector<long>presumsum;
string Ag;
vector<string>dict;
int L;
int R;
const int maxk=1000;//max level of tree
long fnum[maxn][maxk];
long fsum[maxn][maxk];
bool vis[maxn][maxk];
int maxlevel;
long calc_sum_leaf(int num,int k)
{
if(vis[num][k]==true)
{
return fsum[num][k];
}
if(k==maxlevel)
{
fsum[num][k]=num;
vis[num][k]=true;
return num;
}
else
{
long childsum=0;
for(int i=0;i<dict[num].length();i++)
{
childsum+=calc_sum_leaf(dict[num][i]-'0',k+1);
}
fsum[num][k]=childsum;
vis[num][k]=true;
return fsum[num][k];
}
}
long calc_leaf_num(int num,int k)
{
if(vis[num][k]==true)
{
return fnum[num][k];
}
if(k==maxlevel)
{
fnum[num][k]=1;
vis[num][k]=true;
// cout<<"num "<<num<<" level "<<k<<" "<<fnum[num][k]<<endl;
return 1;
}
else
{
long childnum=0;
for(int i=0;i<dict[num].length();i++)
{
childnum+=calc_leaf_num(dict[num][i]-'0',k+1);
}
fnum[num][k]=childnum;
vis[num][k]=true;
// cout<<"num "<<num<<" level "<<k<<" "<<fnum[num][k]<<endl;
return fnum[num][k];
}
}
void initialize(int n, long m, string A, vector<string> S) {
// m=1e17;
// cout<<m<<endl;
dict.clear();
presum.clear();
presumsum.clear();
for(int i=0;i<S.size();i++)
{
dict.push_back(S[i]);
}
Ag=A;
long long cnt=n;
long numfreq[2][maxn];//rolling, one is current, one is next
long currnumfreq[maxn][maxn];//[i,j] num i will leads xxx # of j to appear in the expansion
memset(numfreq,0,sizeof(numfreq));
memset(currnumfreq,0,sizeof(currnumfreq));
for(int i=0;i<10;i++)
{
// cout<<"num "<<i<<endl;
for(int j=0;j<dict[i].size();j++)
{
currnumfreq[i][dict[i][j]-'0']++;
}
// for(int j=0;j<10;j++)
// {
// cout<<currnumfreq[i][j]<<" ";
// }
// cout<<endl;
}
int curr=0;
for(int i=0;i<A.length();i++)
{
numfreq[curr][A[i]-'0']++;
}
cnt=A.length();
for(int i=0;i<maxk;i++)
{
// cout<<"level "<<i<<" cnt "<<cnt<<endl;
if(cnt>=m)
{
maxlevel=i;
break;
}
cnt=0;
int nextcurr=(curr+1)%2;
memset(numfreq[nextcurr],0,sizeof(numfreq[nextcurr]));
for(int j=0;j<10;j++)
{
// cout<<"num "<<j<<" "<<numfreq[curr][j]<<endl;
if(numfreq[curr][j]==0)
{
continue;
}
else
{
for(int k=0;k<10;k++)
{
numfreq[nextcurr][k]+=numfreq[curr][j]*currnumfreq[j][k];
// cout<<"Add "<<k<<" count "<<numfreq[curr][j]*currnumfreq[j][k]<<endl;
}
}
}
for(int j=0;j<10;j++)
{
cnt+=numfreq[nextcurr][j];
}
curr=nextcurr;
}
// long expand=S[0].length();//different S[i] may have different length
// for(int i=0;i<maxk;i++)
// {
//// cout<<i<<" "<<cnt<<endl;
// if(cnt>=m)
// {
// maxlevel=i;
// break;
// }
// cnt*=expand;
// }
//maxlevel=60;
cout<<"max level "<<maxlevel<<endl;
memset(fnum,0,sizeof(fnum));
memset(fsum,0,sizeof(fsum));
memset(vis,false,sizeof(vis));
for(int i=0;i<A.length();i++)
{
calc_sum_leaf(A[i]-'0',0);
}
// cout<<"here0"<<endl;
// for(int j=0;j<=maxlevel;j++)
// {
// for(int i=0;i<10;i++)
// {
// cout<<"num "<<i<<" level "<<j<<" "<<fsum[i][j]<<" "<<endl;
// }
// cout<<endl;
// }
memset(vis,false,sizeof(vis));
for(int i=0;i<A.length();i++)
{
// cout<<"calc num of "<<A[i]<<endl;
calc_leaf_num(A[i]-'0',0);
}
// cout<<"here1"<<endl;
// for(int j=0;j<=maxlevel;j++)
// {
// for(int i=0;i<10;i++)
// {
// cout<<"num "<<i<<" level "<<j<<" "<<fnum[i][j]<<" "<<endl;
// }
// cout<<endl;
// }
//presum of node in the level 0
presum.push_back(fnum[A[0]-'0'][0]);
presumsum.push_back(fsum[A[0]-'0'][0]);
for(int i=1;i<A.length();i++)
{
presum.push_back(presum[i-1]+fnum[A[i]-'0'][0]);
presumsum.push_back(presumsum[i-1]+fsum[A[i]-'0'][0]);
}
// cout<<"presum"<<endl;
// for(int i=0;i<presum.size();i++)
// {
// cout<<presum[i]<<" ";
// }
// cout<<endl;
// cout<<"here2"<<endl;
// for(int i=0;i<presumsum.size();i++)
// {
// cout<<presumsum[i]<<" ";
// }
// cout<<endl;
}
long dfs(long idx,int num,int k)
{
// cout<<idx<<" "<<num<<" "<<k<<endl;
if(k==maxlevel)
{
return (long)num;
}
long ret=0;
for(int i=0;i<dict[num].size();i++)
{
int next=dict[num][i]-'0';
if(fnum[next][k+1]<idx)
{
ret+=fsum[next][k+1];
idx-=fnum[next][k+1];
}
else
{
ret+=dfs(idx,next,k+1);
break;
}
}
return ret;
}
long getpresum(long idx)
{
if(idx==0)
{
return 0;
}
// cout<<"search idx "<<idx<<endl;
int st=lower_bound(presum.begin(),presum.end(),idx)-presum.begin();
// cout<<"start from "<<st<<endl;
if(st==0)
{
return dfs(idx,Ag[st]-'0',0);
}
//start from sub tree in level 0;
else
{
// cout<<"pre sub tree "<<fsum[Ag[st-1]-'0'][0]<<endl;
return presumsum[st-1]+dfs(idx-presum[st-1],Ag[st]-'0',0);
}
}
long query(long l, long r) {
return getpresum(r)-getpresum(l-1);
}
int main()
{
// list<char>arr;
// for(int i=0;i<5;i++)
// {
// arr.push_back(i+'0');
// }
// for(list<int>::iterator iter=arr.begin();iter!=arr.end();)
// {
// if((*iter)==2)
// {
// iter=arr.erase(iter);
// cout<<(*iter)<<endl;
// break;
// }
// else
// {
// iter++;
// }
//// arr.insert(iter,10);
//
// }
// for(list<int>::iterator iter=arr.begin();iter!=arr.end();iter++)
// {
// cout<<(*iter)<<" ";
// }
// cout<<endl;
// return 0;
freopen("input.txt","r",stdin);
// freopen("out.txt","w",stdout);
// freopen("out-cmp.txt","w",stdout);
cin>>T;
for(int ca=1;ca<=T;ca++)
{
cin>>N>>M>>Q;
string A;
vector<string>S;
cin>>A;
cin.ignore();
S.clear();
// cout<<N<<" "<<M<<" "<<Q<<endl;
for(int i=0;i<10;i++)
{
string tmp;
cin>>tmp;
cin.ignore();
// cout<<"tmp "<<tmp<<"ed"<<endl;
S.push_back(tmp);
}
// cin.ignore();
initialize(N,M,A,S);
for(int i=0;i<Q;i++)
{
cin>>L>>R;
// cout<<L<<" "<<R<<endl;
cout<<query(L,R)<<endl;
// cout<<"presum of R "<<R<<" "<<getpresum(R)<<endl;
}
// cout<<"Case #"<<ca<<": "<<query(L,R)<<endl;
}
return 0;
}
附上最初認慫用linked list暴力模擬的程式碼。。
#include<iostream>
#include<stdio.h>
#include<cstdio>
#include<string>
#include<cmath>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
#include<list>
using namespace std;
const int maxn=10;
int T;
int N;
int M;
int Q;
vector<long>presum;
string A;
vector<string>S;
int L;
int R;
const int maxk=60;//max level of tree
void initialize(int n, long m, string A, vector<string> S) {
presum.clear();
// cout<<"A "<<A<<endl;
// for(int i=0;i<S.size();i++)
// {
// cout<<S[i]<<endl;
// }
// cout<<"end input"<<endl;
// vector<char>arr;
list<int>arr;
for(int i=0;i<n;i++)
{
arr.push_back(A[i]-'0');
}
// cout<<"here"<<endl;
int cnt=n;
while(cnt<m)
{
for(list<int>::iterator iter=arr.begin();iter!=arr.end();)
{
int tmp=*iter;
// cout<<tmp<<endl;
iter=arr.erase(iter);
// cout<<"after erase "<<(*iter)<<endl;
// arr.insert(iter,9);
for(int i=0;i<S[tmp].length();i++)
{
arr.insert(iter,S[tmp][i]-'0');
// cout<<"insert "<<S[tmp][i]<<endl;
}
cnt+=S[tmp].length()-1;
}
}
// for(list<int>::iterator iter=arr.begin();iter!=arr.end();iter++)
// {
// cout<<(*iter)<<" ";
// }
// cout<<endl;
list<int>::iterator iter=arr.begin();
presum.push_back(0);
for(int i=1;i<=cnt;i++)
{
// presum[i]=(*iter)+presum[i-1];
presum.push_back((*iter)+presum[i-1]);
iter++;
}
// for(int i=0;i<=cnt;i++)
// {
// cout<<presum[i]<<" ";
// }
// cout<<endl;
}
long query(long l, long r) {
// cout<<r<<" "<<l-1<<" "<<presum[r]<<" "<<presum[l-1]<<endl;
// for(int i=0;i<=M;i++)
// {
// cout<<presum[i]<<" ";
// }
// cout<<endl;
return presum[r]-presum[l-1];
}
int main()
{
// list<char>arr;
// for(int i=0;i<5;i++)
// {
// arr.push_back(i+'0');
// }
// for(list<int>::iterator iter=arr.begin();iter!=arr.end();)
// {
// if((*iter)==2)
// {
// iter=arr.erase(iter);
// cout<<(*iter)<<endl;
// break;
// }
// else
// {
// iter++;
// }
//// arr.insert(iter,10);
//
// }
// for(list<int>::iterator iter=arr.begin();iter!=arr.end();iter++)
// {
// cout<<(*iter)<<" ";
// }
// cout<<endl;
// return 0;
freopen("input.txt","r",stdin);
// freopen("out.txt","w",stdout);
// freopen("out-cmp.txt","w",stdout);
cin>>T;
for(int ca=1;ca<=T;ca++)
{
cin>>N>>M>>Q;
cin>>A;
cin.ignore();
S.clear();
// cout<<N<<" "<<M<<" "<<Q<<endl;
for(int i=0;i<10;i++)
{
string tmp;
cin>>tmp;
cin.ignore();
// cout<<"tmp "<<tmp<<"ed"<<endl;
S.push_back(tmp);
}
// cin.ignore();
initialize(N,M,A,S);
for(int i=0;i<Q;i++)
{
cin>>L>>R;
// cout<<L<<" "<<R<<endl;
cout<<query(L,R)<<endl;
}
cout<<"Case #"<<ca<<": "<<query(L,R)<<endl;
}
return 0;
}